socket阻塞和非阻塞模式
本文讨论Linux下的socket套接字
一、同步、异步阻塞和非阻塞
- 同步:主动请求并等待IO操作完成的方式
- 异步:主动请求数据后,可以去处理其它任务,随后等待IO操作完毕的通知
- 阻塞:线程持续等待资源中数据准备完成,直到返回响应结果
- 非阻塞:线程直接返回结果,不会持续等待资源准备数据结束后才响应结果
总结:
- 同步与异步是指IO操作,同步是线程等待IO的完成,异步是IO完成时线程会收到通知
- 阻塞与非阻塞是指线程,阻塞可能发生在IO期间,也可能发生在IO之前
例如,对于一个简单的服务器程序(socket、bind、listen、accept),我们通常会说程序会阻塞在accept()处,而accept()函数的操作是同步的
二、什么是socket阻塞/非阻塞模式?
1) 建立连接 connect
- 阻塞方式下,connect首先发送SYN请求到服务器,当客户端收到服务器返回的SYN的确认时,则connect返回,否则的话一直阻塞。
- 非阻塞方式,connect将启用TCP协议的三次握手,但是connect函数并不等待连接建立好才返回,而是立即返回,返回的错误码为EINPROGRESS,表示正在进行某种过程。
2)接收连接 accept
- 阻塞模式下调用accept()函数,没有新连接时,进程会进入睡眠状态,直到有可用的连接,才返回。
- 非阻塞模式下调用accept()函数立即返回,有连接返回客户端套接字描述符。没有新连接时,将返回EWOULDBLOCK错误码,表示本来应该阻塞。
3)读写操作 read/write
- 当读写缓冲区被填满后,阻塞模式下程序会阻塞在read/write,而非阻塞模式时read/write会直接返回错误码
三、如何设置socket阻塞/非阻塞模式?
- socket()函数创建的socket默认是阻塞的
- socket()函数原型:
int socket(int domain, int type, int protocol);
/*1.domain:协议域,又称协议族,它决定了socket的地址类型
* 2.type:指定socket类型,它与protocol对应
* 3.protocol:指定传输协议,如IPPROTO_TCP指定传输协议为TCP,IPPTOTO_UDP指定传输协议为UDP,当protocol为0时,会自动选择type类型对应的默认协议
*/
- 将socket设置为非阻塞模式有两种办法:
- 在创建socket时,指定创建的socket为非阻塞(type参数中设置SOCK_NONBLOCK标志)
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
- 使用fcntl()和ioctl()函数设置socket为非阻塞模式
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞