阻塞模型
int recv( SOCKET s, char* buf, int len, int flags ); |
int send( SOCKET s, const char* buf, int len, int flags ); |
这种方式最为大家熟悉,Socket默认的就是阻塞模式。
在recv的时候,Socket会阻塞在那里,直到连接上有数据可读,把数据读到buffer里后recv函数才会返回,不然就会一直阻塞在那里。
如果在主线程中被阻塞,而数据迟迟没有过来,那么程序就会被锁死。这样的问题可以用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差,而且也容易有锁的问题。线程过多,也导致上下文切换过于频繁,导致系统变慢,而且大部分线程是处于非活动状态的话,这就大大浪费了系统的资源。
设置为非阻塞:
int ioctlsocket( IN SOCKET s, IN long cmd, IN OUT u_long FAR * argp ); #define FIONBIO /* set/clear non-blocking i/o */ |
调用ioctlsocket函数设置FIONBIO为1就转为非阻塞模式。
当recv和send函数没有准备好数据时,函数不会阻塞,立即返回错误值,用GetLastError返回的错误码为WSAEWOULDBLOCK,中文解释为“无法立即完成一个非阻挡性套接字的操作”。
当然,这里你可以用非阻塞模拟阻塞模式,就是用while循环不停调用recv,直到recv返回成功为止。这样的效率也不高,但好处在于你能在没接收到数据时,有空进行其他操作,或者直接Sleep。
把套接字设置为非阻塞模式,即告诉系统:在调用Windows socket API时,不让主调线程睡眠,而让函数立即返回。比如在调用recv函数时,即使此时接受缓冲区没有数据,也不会导致线程在recv处等待,recv函数会立即返回。如果没有调用成功函数会返回WSAEROULDBLOCK错误代码。为了接收到数据必须循环调用recv,这也是非阻塞与阻塞模式的主要区别。
unsigned long ul=1; int ret; s=socket(AF_INET,SOCK_STREAM,0); ret=ioctlsocket(s,FIONBIO,(unsigned long *)&ul);//设置成非阻塞模式。 if(ret==SOCKET_ERROR)//设置失败。 { }