(四) 如何将socket设置为非阻塞模式
1. windows平台上无论利用socket()函数还是WSASocket()函数创建的socket都是阻塞模式的:
SOCKET WSAAPI socket(
_In_ int af,
_In_ int type,
_In_ int protocol
);
SOCKET WSASocket(
_In_ int af,
_In_ int type,
_In_ int protocol,
_In_ LPWSAPROTOCOL_INFO lpProtocolInfo,
_In_ GROUP g,
_In_ DWORD dwFlags
);
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);
2. 另外,windows和linux平台上accept()函数返回的socekt也是阻塞的,linux另外提供了一个accept4()函数,可以直接将返回的socket设置为非阻塞模式:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
只要将accept4()最后一个参数flags设置成SOCK_NONBLOCK即可。
3. 除了创建socket时,将socket设置成非阻塞模式,还可以通过以下API函数来设置:
linux平台上可以调用fcntl()或者ioctl()函数,实例如下:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞
参考: http://blog.sina.com.cn/s/blog_9373fc760101i72a.html
但是网上也有文章说(文章链接:http://blog.csdn.net/haoyu_linux/article/details/44306993),linux下如果调用fcntl()设置socket为非阻塞模式,不仅要设置O_NONBLOCK模式,还需要在接收和发送数据时,需要使用MSG_DONTWAIT标志,即在recv,recvfrom和send,sendto数据时,将flag设置为MSG_DONTWAIT。是否有要进行这种双重设定的必要,笔者觉得没有这个必要。因为linux man手册上recv()函数的说明中关于MSG_DONTWAIT说明如下:
Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)).
通过这段话我觉得要么通过设置recv()函数的flags标识位为MSG_DONTWAIT,要么通过fcntl()函数设置O_NONBLOCK标识,而不是要同时设定。
windows上可调用ioctlsocket函数:
int ioctlsocket(
_In_ SOCKET s,
_In_ long cmd,
_Inout_ u_long *argp
);
将cmd参数设置为FIONBIO,*argp=0即设置成阻塞模式,而*argp非0即可设置成非阻塞模式。但是windows平台需要注意一个地方,如果你对一个socket调用了WSAAsyncSelect()或WSAEventSelect()函数后,你再调用ioctlsocket()函数将该socket设置为非阻塞模式,则会失败,你必须先调用WSAAsyncSelect()通过设置lEvent参数为0或调用WSAEventSelect()通过设置lNetworkEvents参数为0来分别禁用WSAAsyncSelect()或WSAEventSelect()。再次调用ioctlsocket()将该socket设置成阻塞模式才会成功。因为调用WSAAsyncSelect()或WSAEventSelect()函数会自动将socket设置成非阻塞模式。msdn上的原话是:
The WSAAsyncSelect and WSAEventSelect functions automatically set a socket to nonblocking mode. If WSAAsyncSelect or WSAEventSelect has been issued on a socket, then any attempt to use ioctlsocket to set the socket back to blocking mode will fail with WSAEINVAL.
To set the socket back to blocking mode, an application must first disable WSAAsyncSelect by calling WSAAsyncSelect with the lEvent parameter equal to zero, or disable WSAEventSelect by calling WSAEventSelect with the lNetworkEvents parameter equal to zero.
网址:https://msdn.microsoft.com/en-us/library/windows/desktop/ms738573(v=vs.85).aspx
4. 在看实际项目中以前一些前辈留下来的代码中,通过在一个循环里面调用fcntl()或者ioctlsocket()函数来socket的非阻塞模式的,代码如下:
for (;;)
{
#ifdef UNIX
on=1;
if (ioctlsocket(id, FIONBIO, (char *)&on) < 0)
#endif
#ifdef WIN32
unsigned long on_windows=1;
if (ioctlsocket(id, FIONBIO, &on_windows) < 0)
#endif
#ifdef VOS
int off=0;
if (ioctlsocket(id, FIONBIO, (char *)&off) <0)
#endif
{
if (GET_LAST_SOCK_ERROR() == EINTR)
continue;
RAISE_RUNTIME_ERROR("Can not set FIONBIO for socket");
closesocket(id);
return NULL;
}
break;
}
是否有必要这样做,有待考证。
- 小程序学习笔记分享(含1-tabBar、轮播图和九宫格)
- Android中ViewStub控件分析及使用
- 如何实现微信小程序的滚动加载功能
- 分享微信小程序推送消息步骤
- 实例分享微信小程序项目搭建(下)
- 实例分享微信小程序项目搭建(上)
- Android6.0源码分析之蓝牙显示接收到的文件
- Android中应用调用系统权限
- Android5.0以后隐式启动ServiceBug
- Android6.0源码分析之录音功能(一)
- Android6.0源码开发之修改默认音量default及max和min
- Android源码开发之添加/删除系统应用
- 按键事件处理
- Android6.0锁屏源码分析之界面布局分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法