千家信息网

服务器编程心得(四)—— 如何将socket设置为非阻塞模式

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,windows平台上无论利用socket()函数还是WSASocket()函数创建的socket都是阻塞模式的:SOCKET WSAAPI socket(_In_ int af,_In_ int ty
千家信息网最后更新 2024年11月11日服务器编程心得(四)—— 如何将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).aspx4. 在看实际项目中以前一些前辈留下来的代码中,通过在一个循环里面调用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;
}

是否有必要这样做,有待考证。
函数 阻塞 模式 参数 平台 必要 代码 数据 文章 标志 标识 要么 成功 再次 前辈 原话 可以通过 同时 地方 实例 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 易语言数据库列表框 网络安全国内外 苏州项目软件开发哪家正规 ns版暗黑2重制版服务器 三五网络技术 护苗网络安全系列小课堂 软件开发将来就业率广吗 物联网技术与网络安全有关系吗 手机应用软件开发者选项 网络安全认证联通的 北京专业软件开发费用是多少 网络安全海报内容三年级小学生 只复制删选后的数据库 软件开发需要的知识和能力 引用ado连接数据库 网安网络安全检查都应该检查什么 数据库怎样修改字段长度 通信软件开发工程师面试题 sql数据库客户端作用 南昌初华互联网科技有限公司 如何学习计算机数据库编程 青海专业软件开发价格 网络安全最基本工作总结 北京拼优品互联网科技怎么样 综合网络技术咨询怎么样 服务器ip访问怎么设置 后端需要设置数据库 网络安全与执法专业好毕业吗 山东艺术学院高考数据库 数据库佩洛西拜登
0