C++ 非阻塞套接字的使用 (3)
异步非阻塞套接字避免了死循环的接收问题,但是软件用起来体验还是很差。究其原因,软件在指令的发送、接收上,
采取了一种不合理的方式:在指令的发送后,立刻调用接收函数,等待回令。
若是采用同步阻塞套接字,那么如果连接出现问题没有回令,那么软件进程会始终等待锁死,这样显然是不合理。于是
采用同步非阻塞的接收方式,timeout被设置为NULL。这时会出现发令与回令的错位问题。这是由于,相应回令没有及时到
达,而接收函数已经返回,第一条发令被当做无回令处理;而此时回令被堆积在了缓冲区,被第二条发令接收到,造成指令错位。
实际上,这种同步的接收方式是不合理的。如果软件有长连接中接收消息的可能性,那么显然单独设置接受线程是更为合理,
这种主动的接受方式优于被动的调用接收函数的方式。在软件的设计中,通信需要下一番功夫,这是个教训。
总之,软件的设计已经无法更改,我们需要缓解这个问题,所以只好采用同步非阻塞的结束方式,使用select()函数实现。
以下内容引用自http://blog.csdn.net/inspiron_110/article/details/4764847。
select模型(选择模型)
先看一下下面的这句代码:
int iResult = recv(s, buffer,1024);
这是用来接收数据的,在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把数据读到
buffer里后recv函数才会返回,不然就会一直阻塞在那里。在单线程的程序里出现这种情况会导致主线程(单线程程序里只有
一个默认的主线程)被阻塞,这样整个程序被锁死在这里,如果永远没数据发送过来,那么程序就会被永远锁死。这个问题可以
用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差。Select模型就是为了解决这个问题而出现的。
再看代码:
int iResult = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);
iResult = recv(s, buffer,1024);
这一次recv的调用不管套接字连接上有没有数据可以接收都会马上返回。原因就在于我们用ioctlsocket把套接字设置为非阻塞模式了。
不过你跟踪一下就会发现,在没有数据的情况下,recv确实是马上返回了,但是也返回了一个错误:WSAEWOULDBLOCK,意思就是请
求的操作没有成功完成。看到这里很多人可能会说,那么就重复调用recv并检查返回值,直到成功为止,但是这样做效率很成问题,开销太大。
感谢天才的微软工程师吧,他们给我们提供了好的解决办法。
先看看select函数
int select(
int nfds,
fd_set FAR *readfds,
fd_set FAR *writefds,
fd_set FAR *exceptfds,
const struct timeval FAR *timeout
);
第一个参数不要管,会被系统忽略的。第二个参数是用来检查套接字可读性,也就说检查套接字上是否有数据可读,同样,第三个参数
用来检查数据是否可以发出。最后一个是检查是否有带外数据可读取。
参数详细的意思请去看MSDN,这里限于篇幅不详细解释了。
最后一个参数是用来设置select等待多久的,是个结构:
struct timeval {
long tv_sec; // seconds
long tv_usec; // and microseconds
};
如果将这个结构设置为(0,0),那么select函数会马上返回。
说了这么久,select的作用到底是什么?
他的作用就是:防止在在阻塞模式的套接字里被锁死,避免在非阻塞套接字里重复检查WSAEWOULDBLOCK错误。
他的工作流程如下:
1:用FD_ZERO宏来初始化我们感兴趣的fd_set,也就是select函数的第二三四个参数。
2:用FD_SET宏来将套接字句柄分配给相应的fd_set。
3:调用select函数。
4:用FD_ISSET对套接字句柄进行检查,如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么
说明马上可以进行相应的IO操作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的
fd_set里,那么说明当前数据已经来了,马上可以读取成功而不会被阻塞。
最后附上一段示例代码:
WSADATA wsa;
WSAStartup(MAKEWORD(,), &wsa);
SOCKET fdServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(InternetAddr);
server.sin_port = htons(iPort);
int ret = bind(fdServer, (sockaddr*)&server, sizeof(server));
ret = listen(fdServer, ); SOCKET AcceptSocket;
fd_set fdread;
timeval tv;
int nSize; while()
{
FD_ZERO(&fdread);//初始化fd_set
FD_SET(fdServer, &fdread);//分配套接字句柄到相应的fd_set
//这里我们打算让select等待两秒后返回,避免被锁死
tv.tv_sec = ;
tv.tv_usec = ;
select(, &fdread, NULL, NULL, &tv);
nSize = sizeof(server); //如果套接字句柄还在fd_set里,说明客户端已经有
connect的请求发过来了,马上可以accept成功
if (FD_ISSET(fdServer, &fdread))
{
AcceptSocket = accept(fdServer,
( sockaddr*) &server, &nSize);
break;
}
} char buffer[];
ZeroMemory(buffer, ); //这里同样可以用select,用法和上面一样
ret = recv(AcceptSocket,buffer,,); closesocket(AcceptSocket);
WSACleanup();
return;
C++ 非阻塞套接字的使用 (3)的更多相关文章
- python_非阻塞套接字及I/O流
http://www.cnblogs.com/lixy-88428977/p/9638949.html 首先,我们要明确2个问题: 普通套接字实现的服务端有什么缺陷吗? 有,一次只能服务一个客户端! ...
- 非阻塞套接字编程, IO多路复用(epoll)
非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...
- 非阻塞套接字与IO多路复用
我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个客户端! 并且,为了使一个客户端能够不断收发消息,我们还要使用while循环来轮询,这极大地降低了我们的效率 acce ...
- 11 非阻塞套接字与IO多路复用(进阶)
1.非阻塞套接字 第一部分 基本IO模型 1.普通套接字实现的服务端的缺陷 一次只能服务一个客户端! 2.普通套接字实现的服务端的瓶颈!!! accept阻塞! 在没有新的套接字来之前,不能处理已经建 ...
- 非阻塞套接字与IO多路复用(转,python实现版)
非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回.epoll工作在非阻塞模式时,才会发挥作用. 我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个 ...
- C++ 非阻塞套接字的使用 (2)
继续话题——软件中的异步非阻塞通讯方式. 由于软件基于MFC开发,所以实现异步通讯时使用了CAsyncSocket类. 首先要了解CAsyncSocket异步机制,引用自 http://blog.cs ...
- C++ 非阻塞套接字的使用 (1)
在维护代码的过程中,发现软件运行的CPU占用率居高不下,在4核的电脑上占用了25%的CPU.查阅资料的得知,这是可能是由于软件中出现了死循环. 经过对软件的一些测试,最终确定了死循环出现的位置——通讯 ...
- 阻塞套接字返回EAGAIN
今天用NDK写了一个通信程序,发现阻塞SOKCET 读写的时候返回了EAGAIN.NDK下PERROR输出为Try Again.查了半天头文件 在网上找到了原因.在此纪录.网址为http://blog ...
- 非阻塞SOCKET套接字connect等待时间的实现
SOCKET cClient; cClient=socket(AF_INET,SOCK_STREAM,0); //设置为非阻塞套接字 int iMode = 1; i ...
随机推荐
- 在css嵌套中的html的table里的字左右不对齐
[现象]AAAA与天数的数字左右不居中 <table border=1 align="center"> <tr> <td width="20 ...
- JavaScript导出excel文件实现
function getXlsFromTbl(inTblId, inWindow, fname) { try { var allStr = ""; var curStr = &qu ...
- JS--垒房子
垒房子的小游戏,还没加上得分选项,这是自己的练习笔记,留作笔记随时查看. <!DOCTYPE html><html> <head> <me ...
- UIImagePickerControllerDelegate---ActionSheet---获得设备型号
<pre name="code" class="java">//IOS主要用的是UIImagePickerControllerDelegate这个事 ...
- hdu 5876 (补图BFS) Sparse Graph
题目:这里 题意: 相当于一开始给一个初始好了的无向完全图给你,然后给让你删除m条边,再给你一个点v,最后问你在剩下的图里从这个点v出发能到达所有边点的最小路径是多少? 一看是所有点的最小路径,一看就 ...
- 用c和c++的方式实现栈
#include <stdio.h> #include <stdlib.h> #include <assert.h> struct LinkNode { int d ...
- android opengl es代码功能
/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Versi ...
- win10下搭建QTP测试环境
安装环境win 10 64位企业版 个人学习用1..net 3.5无法安装更新问题解决:打开windows update 服务2.win10 安装中提示为了对电脑进行保护,已经阻止此应用,请与管理员联 ...
- [linux]windows无法访问samba的安全性问题(关闭selinux)
背景 在某一天重启了虚拟机的 linux 之后,我的 windows 在连接上 samba 之后,点击某些文件夹的时候,会出现没有权限打开的情况.这问题折腾了我一度重新配置了好几次 samba 的配置 ...
- 获取当前运行dll文件的路径
char moduledir[MAX_PATH]; GetModuleFileNameA(GetModuleHandleA("ppdl_BE081_BIW_seal_library.dll ...