2019独角兽企业重金招聘Python工程师标准>>>

异步与非阻塞区别见我的另外一篇文章Socket 同步/异步与阻塞/非阻塞区别

  1. select

  2. WSAAsyncSelect

  3. WSAEventSelect

  4. 重叠(Overlapped)I/O

  5. IOCP:完成端口

Select

首先要使用ioctlsocket设置为非阻塞模式。

然后启动线程,线程中不停select。

WSAAsyncSelect

WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。此模型提供了读写数据能力的异步通知,但不提供异步数据传送。需要在消息响应函数里send(一般为resend)和receive。由于该模型基于Windows消息机制,必须在应用程序中创建窗口。虽然可以在开发中,确定是否显示该窗口。

WSAEventSelect

通常与WSACreateEvent、WSAResetEvent、WSACloseEvent、WSAWaitForMultileEvents和WSAEnumNetworkEvents一起使用,无需创建窗口。WSAWaitForMultileEvents检查是否有Event,WSAEnumNetworkEvents枚举事件类型,FD_READ、FD_WRITE等。

函数最多可以支持WSA_MAXIMUM_WAIT_EVENTS(64)个对象.该函数会等待网络事件的发生,如果过了指定了时间(dwTimeOut)则返回WSA_WAIT_TIMEOUT,如果在规定的时间内有事件发生,则返回该事件对象的索引(注意:在程序中要想得到发生的事件的真正索引需得用返回值减去WSA_WAIT_EVENT_0),调用失败返回WSA_WAIT_FAILED.如果将参数fWaitAll设置成false如果有多个网络事件发生该函数也只返回一个事件对象索引,并且该事件是在事件句柄数组中最前面的一个.解决方法是循环调用该函数处理后面的受信事件.

重叠(Overlapped)I/O

它和之前模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据,也就是说,如果应用程序投递了一个10KB大小的缓冲区来接收数据,且数据已经到达套接字,则该数据将直接被拷贝到投递的缓冲区。之前的模型都是在套接字的缓冲区中,当通知应用程序接收后,在把数据拷贝到程序的缓冲区。

——摘自http://zhoumf1214.blog.163.com/blog/static/5241940201211705318496/

以receive为例,之前的模型,需要自己从套接字的缓冲区拷贝至程序缓冲区,而重叠IO则是操作系统直接将数据拷贝至程序缓冲区。

重叠模型的核心是一个重叠数据结构。若想以重叠方式使用文件,必须用 FILE_FLAG_OVERLAPPED标志打开它。

有2种方式实现:

1. 事件

先WaitForSingleObject/WaitForMultipleObjects或WSAWaitForMultipleEvents函数 ,然后调用(WSA)GetOverlappedResult()函数,最后,使用指针偏移定位就可以准确操作接受到的数据了。

与其他事件类似,最大个数为64.

2. 完成例程(非完成端口)

ReadFileEx(),传递回调函数指针。

完成端口

所谓“完成端口",实际是Win32 、Windows NT以及Windows 2000采用的一种I / O构造机制,除套接字句柄之外,实际上还可接受其他东西(重叠IO好像也可以)。

使用这种模型之前,首先要创建一个 I / O 完成端口对象(CreateIoCompletionPort),

NumberOfConcurrentThread参数的特殊之处在于,它定义了在一个完成端口上,同时允许执行的线程数量。理想情况下,我们希望每个处理器各自负责一个线程的运行,为完成端口提供服务,避免过于频繁的线程“场景”切换。若将该参数设为 0,表明系统内安装了多少个处理器,便允许同时运行多少个线程!

1)  创建一个完成端口。第四个参数保持为 0,指定在完成端口上,每个处理器一次只允许执行一个工作者线程。

2)  判断系统内到底安装了多少个处理器。

3)  创建工作者线程,根据步骤 2 )得到的处理器信息,在完成端口上,为已完成的 I / O请求提供服务。在这个简单的例子中,我们为每个处理器都只创建一个工作者线程。这是由于事先已预计到,到时不会有任何线程进入“挂起”状态,造成由于线程数量的不足,而使处理器空闲的局面(没有足够的线程可供执行) 。调用C r e a t e T h r e a d函数时,必须同时提供一个工作者例程,由线程在创建好执行。本节稍后还会详细讨论线程的职责。

4)  准备好一个监听套接字,在端口 5 1 5 0上监听进入的连接请求。

5) 使用a c c e p t函数,接受进入的连接请求。

6)  创建一个数据结构,用于容纳“单句柄数据” ,同时在结构中存入接受的套接字句柄。

7)  调用C r e a t e I o C o m p l e t i o n P o r t,将自a c c e p t返回的新套接字句柄同完成端口关联到一起。通过完成键(C o m p l e t i o n K e y)参数,将单句柄数据结构传递给 C r e a t e I o C o m p l e t i o n P o r t。

8)  开始在已接受的连接上进行 I / O 操作。在此,我们希望通过重叠 I / O机制,在新建的套接字上投递一个或多个异步 W S A R e c v或W S A S e n d请求。这些 I / O 请求完成后,一个工作者线程会为I / O请求提供服务,同时继续处理未来的 I / O 请求,稍后便会在步骤 3 )指定的工作者例程中,体验到这一点。

9)  重复步骤5 ) ~ 8 ),直至服务器中止。

// IOCP_TCPIP_Socket_Server.cpp#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#include <iostream>using namespace std;#pragma comment(lib, "Ws2_32.lib")       // Socket编程需用的动态链接库
#pragma comment(lib, "Kernel32.lib")  // IOCP需要用到的动态链接库/*** 结构体名称:PER_IO_DATA* 结构体功能:重叠I/O需要用到的结构体,临时记录IO数据**/
const int DataBuffSize  = 2 * 1024;
typedef struct
{OVERLAPPED overlapped;WSABUF databuff;char buffer[ DataBuffSize ];int BufferLen;int operationType;
}PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;/*** 结构体名称:PER_HANDLE_DATA* 结构体存储:记录单个套接字的数据,包括了套接字的变量及套接字的对应的客户端的地址。* 结构体作用:当服务器连接上客户端时,信息存储到该结构体中,知道客户端的地址以便于回访。**/
typedef struct
{SOCKET socket;SOCKADDR_STORAGE ClientAddr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;// 定义全局变量
const int DefaultPort = 6000;
vector < PER_HANDLE_DATA* > clientGroup;      // 记录客户端的向量组HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);
DWORD WINAPI ServerSendThread(LPVOID IpParam);// 开始主函数
int main()
{
// 加载socket动态链接库WORD wVersionRequested = MAKEWORD(2, 2); // 请求2.2版本的WinSock库WSADATA wsaData;   // 接收Windows Socket的结构信息DWORD err = WSAStartup(wVersionRequested, &wsaData);if (0 != err){    // 检查套接字库是否申请成功cerr << "Request Windows Socket Library Error!\n";system("pause");return -1;}if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){// 检查是否申请了所需版本的套接字库WSACleanup();cerr << "Request Windows Socket Version 2.2 Error!\n";system("pause");return -1;}// 创建IOCP的内核对象/*** 需要用到的函数的原型:* HANDLE WINAPI CreateIoCompletionPort(*    __in   HANDLE FileHandle,        // 已经打开的文件句柄或者空句柄,一般是客户端的句柄*    __in   HANDLE ExistingCompletionPort,    // 已经存在的IOCP句柄*    __in   ULONG_PTR CompletionKey,  // 完成键,包含了指定I/O完成包的指定文件*    __in   DWORD NumberOfConcurrentThreads // 真正并发同时执行最大线程数,一般推介是CPU核心数*2* );**/HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0);if (NULL == completionPort){ // 创建IO内核对象失败cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 创建IOCP线程--线程里面创建线程池// 确定处理器的核心数量SYSTEM_INFO mySysInfo;GetSystemInfo(&mySysInfo);// 基于处理器的核心数量创建线程for(DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i){// 创建服务器工作器线程,并将完成端口传递到该线程HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);if(NULL == ThreadHandle){cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;system("pause");return -1;}CloseHandle(ThreadHandle);}// 建立流式套接字SOCKET srvSocket = socket(AF_INET, SOCK_STREAM, 0);// 绑定SOCKET到本机SOCKADDR_IN srvAddr;srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);srvAddr.sin_family = AF_INET;srvAddr.sin_port = htons(DefaultPort);int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));if(SOCKET_ERROR == bindResult){cerr << "Bind failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 将SOCKET设置为监听模式int listenResult = listen(srvSocket, 10);if(SOCKET_ERROR == listenResult){cerr << "Listen failed. Error: " << GetLastError() << endl;system("pause");return -1;}// 开始处理IO数据cout << "本服务器已准备就绪,正在等待客户端的接入...\n";// 创建用于发送数据的线程HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);while(true){PER_HANDLE_DATA * PerHandleData = NULL;SOCKADDR_IN saRemote;int RemoteLen;SOCKET acceptSocket;// 接收连接,并分配完成端,这儿可以用AcceptEx()RemoteLen = sizeof(saRemote);acceptSocket = accept(srvSocket, (SOCKADDR*)&saRemote, &RemoteLen);if(SOCKET_ERROR == acceptSocket){ // 接收客户端失败cerr << "Accept Socket Error: " << GetLastError() << endl;system("pause");return -1;}// 创建用来和套接字关联的单句柄数据信息结构PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)); // 在堆中为这个PerHandleData申请指定大小的内存PerHandleData -> socket = acceptSocket;memcpy (&PerHandleData -> ClientAddr, &saRemote, RemoteLen);clientGroup.push_back(PerHandleData);      // 将单个客户端数据指针放到客户端组中// 将接受套接字和完成端口关联CreateIoCompletionPort((HANDLE)(PerHandleData -> socket), completionPort, (DWORD)PerHandleData, 0);// 开始在接受套接字上处理I/O使用重叠I/O机制// 在新建的套接字上投递一个或多个异步// WSARecv或WSASend请求,这些I/O请求完成后,工作者线程会为I/O请求提供服务  // 单I/O操作数据(I/O重叠)LPPER_IO_OPERATION_DATA PerIoData = NULL;PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));ZeroMemory(&(PerIoData -> overlapped), sizeof(OVERLAPPED));PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;PerIoData->operationType = 0;   // readDWORD RecvBytes;DWORD Flags = 0;WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);}system("pause");return 0;
}// 开始服务工作线程函数
DWORD WINAPI ServerWorkThread(LPVOID IpParam)
{HANDLE CompletionPort = (HANDLE)IpParam;DWORD BytesTransferred;LPOVERLAPPED IpOverlapped;LPPER_HANDLE_DATA PerHandleData = NULL;LPPER_IO_DATA PerIoData = NULL;DWORD RecvBytes;DWORD Flags = 0;BOOL bRet = false;while(true){bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);if(bRet == 0){cerr << "GetQueuedCompletionStatus Error: " << GetLastError() << endl;return -1;}PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);// 检查在套接字上是否有错误发生if(0 == BytesTransferred){closesocket(PerHandleData->socket);GlobalFree(PerHandleData);GlobalFree(PerIoData);continue;}// 开始数据处理,接收来自客户端的数据WaitForSingleObject(hMutex,INFINITE);cout << "A Client says: " << PerIoData->databuff.buf << endl;ReleaseMutex(hMutex);// 为下一个重叠调用建立单I/O操作数据ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空内存PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;PerIoData->operationType = 0;    // readWSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);}return 0;
}// 发送信息的线程执行函数
DWORD WINAPI ServerSendThread(LPVOID IpParam)
{while(1){char talk[200];gets(talk);int len;for (len = 0; talk[len] != '\0'; ++len){// 找出这个字符组的长度}talk[len] = '\n';talk[++len] = '\0';printf("I Say:");cout << talk;WaitForSingleObject(hMutex,INFINITE);for(int i = 0; i < clientGroup.size(); ++i){send(clientGroup[i]->socket, talk, 200, 0); // 发送信息}ReleaseMutex(hMutex); }return 0;
}

转载于:https://my.oschina.net/shanlilaideyu/blog/485349

Windows 非阻塞或异步 socket相关推荐

  1. python 网络编程----非阻塞或异步编程

    From: http://blog.chinaunix.net/uid-20730371-id-765038.html 非阻塞或异步编程 例如,对于一个聊天室来说,因为有多个连接需要同时被处理,所以很 ...

  2. Python|线程和进程|阻塞|非阻塞|同步|异步|生成器和协程|资源竞争|进程间通信|aiohttp库|daemon属性值详解|语言基础50课:学习(11)

    文章目录 系列目录 原项目地址 第34课:Python中的并发编程-1 线程和进程 多线程编程 使用 Thread 类创建线程对象 继承 Thread 类自定义线程 使用线程池 守护线程 资源竞争 G ...

  3. 那些年让你迷惑的阻塞、非阻塞、异步、同步

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 在IT圈混饭吃,不管你用什么编程语言.从事前端还是后端,阻塞.非阻塞.异步.同步这些概念,都需要清 ...

  4. node - 非阻塞的异步 IO

    node - 非阻塞的异步 IO 每当我们提起 node.js 时总会脱口而出 事件驱动.非阻塞I/O 和 单线程,所以我总结了以下几点对这三项概念的阐述,不一定正确仅仅代表个人观点. 单线程 当一个 ...

  5. 异步同步、阻塞非阻塞、异步回调、线程队列和协程

    今天学习了异步同步.阻塞非阻塞.异步回调.线程队列和协程 一.异步同步和阻塞非阻塞 线程的三种状态: 1.就绪 2.运行 3.阻塞 阻塞:遇到了IO操作  代码卡住  无法执行下一行  CPU会切换到 ...

  6. 同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...

  7. 同步阻塞、同步非阻塞、异步阻塞、异步非阻塞与 I/O 多路复用、Java NIO 之间的联系

    同步阻塞.同步非阻塞.异步阻塞.异步非阻塞与 I/O 多路复用.Java NIO 之间的联系 先验知识 此处的异步指的是什么 同步.异步.阻塞.非阻塞 同步阻塞.同步非阻塞.异步阻塞.异步非阻塞 一个 ...

  8. 非阻塞式异步Java 8和Scala的Try / Success / Failure

    受Heinz Kabutz最近的时事通讯以及我在最近的书中研究的Scala的期货的启发,我着手使用Java 8编写了一个示例,该示例如何将工作提交给执行服务并异步地响应其结果,并使用了回调.无需阻止任 ...

  9. recvfrom函数 非阻塞_那些年让你迷惑的阻塞、非阻塞、异步、同步

    那些年让你迷惑的阻塞.非阻塞.异步.同步 在IT圈混饭吃,不管你用什么编程语言.从事前端还是后端,阻塞.非阻塞.异步.同步这些概念,都需要清晰地掌握,否则,怎么与面试官谈笑风生(chui niu pi ...

最新文章

  1. cvDrawContours:在图像上绘制外部和内部轮廓
  2. 推荐算法是今日头条的核心竞争力吗?
  3. 动画制作c语言程序,C语言动画制作
  4. R树空间索引及其变种
  5. 作者:李喜莲(1992-),女,北京大学信息科学技术学院硕士生。
  6. 一般是一个较为复杂的 飞鸽传书 对象
  7. 不显示样式 引用bootstrap_Bootstrap的引用样式
  8. C语言简明数据类型指南
  9. python程序设计和c语言_C 语言和 Python,该从哪个入门编程?
  10. 微课计算机教学研究案例,《微课在中学信息技术教学中有效运用的案例研究》的开题报告...
  11. python转换成exe后会出现dos框_解决Pyinstaller 打包exe文件 取消dos窗口(黑框框)的问题...
  12. python excel数据分析师职业技能_数据分析师=Excel+Python?其实不止!
  13. 心理学经典理论与著作
  14. 客户细分_客户细分初学者指南
  15. 写在冬日的第一天--一个女程序员第八年工作总结
  16. jQuery读取Table表格数据
  17. vue3.0需要学习的技术栈
  18. svg中 path标签的d属性
  19. Android漏洞查询
  20. 华为某高管工资曝光:每月高达27万,众网友表示长了见识

热门文章

  1. 设python中有模块m_Python 模块
  2. 腾讯翻译君在线翻译怎么翻译整个文件_藏语怎么翻译成中文?这两方法非常好用...
  3. 安卓编程用什么软件_震惊!安卓IOS都可以用的牛逼软件
  4. python 多继承 __new___Python3中的__new__方法以及继承不可变类型类的问题
  5. python 函数重载_在Python中实现函数重载,60%的人都不会
  6. python的字符串_百度资讯搜索_python的字符串
  7. java宏定义_现代化的 Java (二十六)—— Akka Stream Graph
  8. 大专计算机系毕业课题6000字,计算机理论论文6000字:高校科研.doc
  9. linux lamp架构部署,Centos7部署LAMP平台之架构之路
  10. element的滚动去掉横向_textarea去掉滚动条 textarea横向或纵向滚动条的去掉方法