Linux socket实现非阻塞型通信

非阻塞通信方法

对一个文件描述符指定的文件或设备, 有两种工作方式: 阻塞与非阻塞。所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待状态, 直到有东西可读或者可写为止。而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待。缺省情况下, 文件描述符处于阻塞状态。在实现聊天室时, server 需要轮流查询与各client 建立的 socket, 一旦可读就将该 socket 中的字符读出来并向所有其他client 发送。并且, server 还要随时查看是否有新的 client 试图建立连接,这样, 如果 server 在任何一个地方阻塞了, 其他 client 发送的内容就会受到影响,得不到服务器的及时响应。新 client 试图建立连接也会受到影响。所以我们在这里不能使用缺省的阻塞的文件工作方式,而需要将文件的工作方式变成非阻塞方式。

1. fcntl 方法

函数fcntl()可以用来改变文件I/O操作的工作方式,函数描述如下:

fcntl( sockfd, F_SETFL, O_NONBLOCK); 

参数说明:
// sockfd 是要改变状态的文件描述符. 
// F_SETFL 表明要改变文件描述符的状态 
// O_NONBLOCK 表示将文件描述符变为非阻塞的.

 

  

2. select方法

select这个方法用来检测一个socket是否有数据到来或是是否有准备好的数据要发送。声明如下:

select(s, &read_flags, &write_flags, &exec_flags, timer);

参数说明:
//s             socket的句柄
//read_flags    读描述字集合。检查socket上是否有数据可读。
//write_flags   写描述字集合。检查socket上是否已有数据可发送。
//exec_flags    错误描述字集合。(本教程这儿不介绍)
//timer         等待某个条件为真时超时时间。

使用:

select(s, &read_flags, &write_flags, NULL, timer);
 
//exec_flags参数设为null

3.ioctl

其实有个大牛说使用ioctl出现意想不到的问题:

有一个非常有迷惑性的做法是:

u_long has = 1;

ioctl(m_sock, FIONBIO , &has);

这个函数会非常无耻的返回你success,但是它实际上很可能什么也没做。

正确的做法应该是使用fcntl:

int flags = fcntl(m_sock, F_GETFL, 0);

fcntl(m_sock, F_SETFL, flags|O_NONBLOCK);

这真是一个隐蔽的问题,折腾了我两天。线程每每停留在send()调用那里,我始终没怀疑到:

用ioctl设置FIONBIO成功之后,socket竟然还是阻塞的。

原文链接:http://blog.csdn.net/wwwsq/article/details/970198