socket阻塞和非阻塞

来源:互联网 发布:最全家庭网络投资理财 编辑:程序博客网 时间:2024/05/16 07:18

Linux的网络编程有很多种写法,最简单的是阻塞式(Blocking)的网络程序,其次有非阻塞(Non-Blocking),多路复用(Multiplexing),异步(Asynchronous)模型等。

阻塞模型:

阻塞模型是最为简单的一种网络编程模型。顾名思义,该模型中,socket都工作在阻塞模式下,在调用相应的网络函数时,如accept, recv, send时,这些函数都会阻塞线程,直到socket可读或者可写,或者出错时,线程才会继续执行。
在阻塞模式下,我们一般用多线程来对每一个socket进行处理,一个线程服务一次链接,这种模型在一些web服务器中较为常见,如apache,tomcat等

这里写图片描述
扩展pthread_create函数:

int pthread_create(pthread_t *thread,pthread_attr_t *attr,(void *(*start_routine)(void *),void *arg));

其中第二个参数设置线程属性(线程栈大小,线程优先级等)
第三个参数为线程函数的起始地址
第四个参数为传递给线程函数的参数

会阻塞的socket的API:
accept,connect,recv(recvfrom),send(sendto),closesocket,select(poll/epoll)

非阻塞模型:

非阻塞模型,顾名思义,socket工作在非阻塞模式中,这时对该socket调用相应的函数时,并不会阻塞线程运行,即便没有数据准备好,这些函数也会立即返回,线程将继续执行。那我们将需要在后面的代码中,对返回的结果进行判断,以区分刚才的调用是否有数据可以处理。

非阻塞模型的优点在于,可以在一个线程中处理多个socket事件,而不会阻塞线程,减少多线程切换带来的开销。相应的,也增加了处理的复杂度。

设置为非阻塞
int flags = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flags | O_NONBLOCK);
设置为阻塞
int flags = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL,flags &~ O_NONBLOCK);

非阻塞socket代码逻辑(recv):

while(1){    char buff[1024] = {0};    int num = recv(fd,buff,sizeof(buff),0);    if(num > 0)    {        //recv成功 接收到数据    }    else if(num < 0)    {        if((errno == EAGAIN || errno == EWOULDBLOCK))        {            //非阻塞 连接正常 EAGAIN EWOULDBLOCK 表示在没有数据到来时的返回            //可以处理一些事件            continue;        }        else        {            //error            break;        }    }}

阻塞和非阻塞函数行为差异:

  • accept在阻塞模式下,没有新连接时,线程会进入睡眠状态;非阻塞模式下没有新连接立即返回并设置errno为WOULDBLOCK
  • connect在阻塞模式下,仅TCP连接建立成功或出错时才返回;非阻塞模式下,该函数会立即返回INPROCESS错误(需用select检测该连接是否建立成功)
  • recv/recvfrom/send/sendto很好理解,因为这两类函数读写socket文件描述符的接收/发送缓冲区
  • closesocket也不是真正意义上的阻塞,它其实是指是否等待关闭,它受套接字选项SO_LINGER和SO_DONTLINGER的影响。若SO_DONTLINGER或SO_LINGER的间隔=0时,closesocket就是非等待关闭的,但是当SO_LINGER的间隔>0时,closesoket就是等待关闭的,直到剩余数据都发送完毕或直到超时才退出。(但是这个地方只有对于阻塞的套接口才有用,如果是非阻塞的套接口,它会立即返回并且指示错误WOULDBLOCK)
原创粉丝点击