【服务器系统设计】socket的阻塞模式和非阻塞模式总结
前言
对socket在阻塞和非阻塞模式下各个socket函数的表现进行深入理解,是掌握网络编程的基本要求之一,也是重点和难点。
在阻塞和非阻塞模式下,我们常常讨论的具有不同行为表现的socket函数一般有connect,accept,send和recv。
定义
阻塞模式:指的是当某个函数执行成功的条件当前不满足时,该函数会阻塞当前执行线程,程序执行流在超时时间到达或执行成功的条件满足后恢复继续执行。
非阻塞模式:即使某个函数执行成功的条件不满足,该函数也不会阻塞当前执行线程,而是立即返回,继续执行程序流。
如何将socket设置为非阻塞模式
无论是在Windows还是Linux,默认创建的socket都是阻塞模式的。
在Linux上,可以通过使用fcntl函数或者ioctl函数给创建的socket增加O_NONBLOCK标志来将socket设置为非阻塞模式,示例代码如下:
int oldSocketFlag = fcntl(socketfd, F_GETFL, 0);
int newSocketFlag = oldSocketFlag | O_NONBLOCK;
fcntl(socketfd, F_SETFL, newSocketFlag);
当然,Linux上的socket函数也可以在创建时将socket设置为非阻塞模式,socket函数签名如下:
int socket(int domain, int type, int protocol);
只要给type参数增加一个SOCK_NONBLOCK标志即可,例如:
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
不仅如此,在Linux上利用accept函数返回的代表与客户端通信的socket也提供了一个扩展函数accept4,直接将accept函数返回的socket设置为非阻塞的:
int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);
int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen, int flags);
只需要将accept4函数最后一个参数设置为SOCK_NONBLOCK即可。如下:
socklen_t addrlen = sizeof(clientaddr);
int clientfd = accept4(listenfd, &clientaddr, &addrlen, SOCK_NONBLOCK);
send函数和recv函数在阻塞和非阻塞模式下的表现
我们先来想一个问题,当程序调用了send函数,是不是就意味着将数据成功发送到网络上了?
其实,send和recv函数的命名其实有一种误导效果。
- send函数从本质上来说并不是向网络发送数据,而是将应用层发送缓冲区的数据拷贝到内核缓冲区中,至于数据什么时候会从网卡缓冲区中真正地发送到网络中,要根据TCP/IP的协议栈的行为来确定。如果socket设置了TCP_NODELAY选项(即禁用nagel算法),存放到内核缓冲区的数据就会被立即发送出去;反之,一次放入内核缓冲区的数据包如果太小,则系统会在多个小的数据包凑成一个足够大的数据包后才会将数据发送出去。
- recv函数在本质上并不是从网络收取数据,而是将内核缓冲区中的数据拷贝到应用程序的缓冲区中,当然,拷贝完成后会将内核缓冲区中的该部分的数据移除。
那么我们来想一种情况,假如说,有一个应用程序A跟应用程序B建立了TCP通信,而应用程序不断调用send函数,那么数据将会不断地拷贝到对应的内核缓冲区中,如果应用程序一直不调用recv函数,那么在应用程序B的内核缓冲区被填满后,应用程序A的内核缓冲区也会被填满,此时应用程序A继续调用send函数会是什么结果呢?具体的结果取决于socket是否是阻塞模式
(1)当socket是阻塞模式时,继续调用send/recv函数,程序会阻塞在send/recv调用处
(2)当socket是非阻塞模式时,继续调用send/recv函数,send/recv函数不会阻塞程序执行流,而是立即出错并返回,在Linux上会返回一个EWOULDBLOCK或者EAGAIN(两个错误码值相同)错误码。
非阻塞模式下如何正确地调用send/recv函数
由于非阻塞模式会让程序的执行流变得复杂,不像阻塞模式下那么简单明了。
以send函数为例,有人可能会写出这样的程序:
int n = send(socket, buf, buf_length, 0);
if(n == buf_length)
{std::cout << "send data succ!" << std::endl;
}
其实这种写法是不推荐的,正确地写法应该这样:
bool sendData(const char* buf, int buf_length)
{int send_bytes = 0; //已发送的字节数int ret = 0;while(true){ret = send(socket_, buf + send_bytes, buf_length - send_bytes, 0);if(ret == -1){if(errno == EWOULDBLOCK){//缓存尚未发送出去的数据break;}else if(errno == EINTR){continue;}else{return false;}}else if(ret == 0){return false;}send_bytes += ret;if(send_bytes == buf_length){break; }}return true;
}
阻塞与非阻塞模式socket各自的应用场景
阻塞的socket函数在调用send、recv、connect、accept等函数时,如果条件不满足,就会阻塞其调用线程直至超时,非阻塞的socket则会立即返回。但这并不意味着非阻塞模式一定比阻塞模式好,两者各有优缺点。
非阻塞一般用于需要支持高并发的场景,但这种模式会让程序的执行流变得复杂,相反,阻塞模式的使用逻辑十分简单,程序结构简单明了,可以用在一些特殊场景中。
适合非阻塞模式的应用场景:
- 需要支持高并发程序,如服务端程序
适合阻塞模式下的应用场景:
- 某程序需要临时发送一个文件,文件分段发送,每发送一段,对端都会给予一个响应。
- 程序A和程序B之间的通信只有问答模式,即A端每发送给B端一个请求,B端必定会给A端一个响应,除此之外,B端不会向A端推送任何数据,此时A端就可以采用阻塞模式。
【服务器系统设计】socket的阻塞模式和非阻塞模式总结相关推荐
- socket的阻塞模式和非阻塞模式(send和recv函数在阻塞和非阻塞模式下的表现)
socket的阻塞模式和非阻塞模式 无论是Windows还是Linux,默认创建socket都是阻塞模式的 在Linux中,可以再创建socket是直接将它设置为非阻塞模式 int socket (i ...
- socket的阻塞模式和非阻塞模式
文章目录 socket的阻塞模式和非阻塞模式 如何将socket设置为非阻塞模式 send和recv函数在阻塞和非阻塞模式下的表现 非阻塞模式下send和recv函数的返回值总结 阻塞与非阻塞sock ...
- 【gev】 Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 轻量.快速的 Golang 网络库 https://github.com/Allenxuxu/gev gev 是一个轻量.快速的基于 Reactor 模式的非阻塞 TCP 网络库,底层并不使用 ...
- golang mysql 非阻塞_Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库...
gev 轻量.快速的 Golang 网络库 gev 是一个轻量.快速的基于 Reactor 模式的非阻塞 TCP 网络库,底层并不使用 golang net 库,而是使用 epoll 和 kqueue ...
- Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结
同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 由处理消息者自己去等待消息是否被触发 我去银行办理业务,选择排队等,排到头了就办理. 异步模式 由触发机制来通知处理消息者 我去银行办 ...
- 阻塞io阻塞io_Redis:RESP协议,阻塞IO 与非阻塞IO,Redis的线程模型
1.Redis 阻塞IO 与非阻塞IO Java在JDK1.4 中引入了NIO ,但是也有很多人在使用阻塞IO,这两种IO有什么区别? 在阻塞模式下,如果你从数据流读取不到指定大小的数据量,IO就会阻 ...
- 【多线程】0.理解一下5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
5种IO模型.阻塞IO和非阻塞IO.同步IO和异步IO 看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度.环境不一样.所以,我们先说明基本的IO操作及环境. 本文是在<UNIX网络编 ...
- 5种网络IO模型:阻塞IO、非阻塞IO、异步IO、多路复用IO、信号驱动IO
目录 前言 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) 模型间的区别 ...
- 阻塞IO、非阻塞IO、以及多路复用原理
阻塞IO.非阻塞IO.以及多路复用原理 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 阻塞IO.非阻塞IO.以及多路复用原理 什么是I/O 一.BIO(阻塞IO) 二.N ...
- 阻塞IO与非阻塞IO
学习地址:https://www.cnblogs.com/xiaoxi/p/6525396.html 在<Unix网络编程>一书中提到了五种IO模型,分别是:阻塞IO.非阻塞IO.多路复用 ...
最新文章
- 【他们都说 select * 不好,但是 。。。】
- 打开高效文本编辑之门_Linux Awk之条件判断与循环
- python多级网址爬取_python-29:多级页面爬取源码
- 437. 路径总和 III
- 对于一组给定的叶子结点_高糊图片可以做什么?Goodfellow等人用它生成一组合理图像...
- DB2 SQL性能调优秘笈
- TTS Service Extended (进程:com.google.tts)意外停止 恢复被阉割的TTS文字转语音功能
- 我,37岁程序员,依然可以“横行职场”
- Linux磁盘管理:LVM逻辑卷管理
- 【vscode】——程序运行时添加环境变量
- python实时显示进度条_Python进度条实时显示处理进度的示例代码
- ppt背景图片php,ppt模板ppt背景图片 淡雅风格打包下载 (ppt如何打包)
- 【工具篇】AS连接不上夜神模拟器
- hive3.1.2 flink写数据到hive报错Reading or writing ACID table.....
- 使用gsds绘制基因结构图_使用 GSDS 绘制基因结构图
- 计算机模拟专业委员会,中国化工学会过程模拟及仿真专业委员会成立
- can总线不加末端电阻_RS485总线专题讲解,从原理入手!
- android studio 登陆ui界面设计
- matlab作时间轴有关的图
- 在线文档上传图片失真、压缩、清晰度降低怎么办?
热门文章
- 计算机二级c语言word答案,全国计算机考试二级C语言南开100题答案(Word版)
- 全球最快下载工具 XDM
- React Native--移动端开发的救星
- Unity热更之旧项目救星——Xlua热补丁修复
- c语言题模板大全,C语言试题库完整版整理版
- php 快递打印设置,让ecshop批量打印快递单修改方法
- 三分钟明白 Activiti工作流 -- java运用
- PDF怎么转CAD?分享两种转换方法
- PHP fuser,打印机提示 50.1 fuser error 这样的错误,无法正常打印?
- 模型计算机微指令总表,基于微程序控制器的模型计算机设计