windows下的IOCP

匿名 (未验证) 提交于 2019-12-03 00:34:01

对应Linux下的epoll,windows下也有实现IO复用的方法,那就是IOCP,又叫重叠IO,其实和异步IO没什么区别,《TCPIP网络编程》这本书也没有对重叠IP和异步IO做明确的区分。
首先,创建重叠IO的套接字。

SOKET WSAocket(int af,int type,int protocol,LPWSAPROTOCOL_INFO lpProtocolInfo,GROUP g,DWORD dwFlags);

成功时返回套接字句柄,失败时返回INVALID_SOKET。
af 协议族信息
前三个参数和普通套接字一致,后三个分别为
lpProtocolInfo 包含创建的套接字信息的WSAPROTOCOL_INFO结构体变量地址值,不需要时传递NULL。
g 为扩展函数而预约的参数,可以使用0。
dwFlags 套接字属性信息。

执行重叠IO的WSASend函数

int WSASend(SOKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesSent,DWORD dwFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

1-7参数含义分别为:
1:套接字句柄
2:WSABUF结构体变量数组的地址值,其中存有待传输数据。
3:第二个参数中的数组长度
4:用于保存实际发送字节数的变量地址值
5:用于更改数据传输特性
6:WSAOVERLAPPED结构体变量的地址值,使用事件对象,用于确认完成数据传输。
7.传入Completion Routine函数的入口地址值,可以通过该函数确认是否完成数据传输。
接下来介绍第二个结构体参数类型
typedef struct __WSABUF
{
u_long len;
char FAR*buf;
}
可见其中存有缓冲数据的内容和长度。
第六个参数结构体如下:
typedef struct _WSAOVERLAPPE
{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
} WSAOVERLAPPED,*LPWSAOVERLAPPED;
大家想,WSASEND函数中参数lpNumberOfBytesSent可以获得实际传输数据的大小,既然WSASEND能实现异步调用,那么它就要在调用后立即返回,这时候可能还没传输完呢,那么它是如何得到传输数据的大小的呢?因此,在介绍WSARecv之前,我先介绍一个小函数:

      BOOL WSAGetOverlappedResult(                        SOCKET s,                                                // SOCKET,不用说了                        LPWSAOVERLAPPED lpOverlapped,  // 这里是我们想要查询结果的那个重叠结构的指针                        LPDWORD lpcbTransfer,                       // 本次重叠操作的实际接收(或发送)的字节数                        BOOL fWait,                // 设置为TRUE,除非重叠操作完成,否则函数不会返回                                                             // 设置FALSE,而且操作仍处于挂起状态,那么函数就会返回FALSE                                                             // 错误为WSA_IO_INCOMPLETE                                                             // 不过因为我们是等待事件传信来通知我们操作完成,所以我们这里设                                                            // 置成什么都没有作用                        LPDWORD lpdwFlags       // 指向DWORD的指针,负责接收结果标志                      ); 

进行重叠IO的WSARecv函数

int WSARecv(SOCKET s,LPWSABUF lpBuffers,DWORD dwBufferCount,LPDWORD lpNumberOfBytesRecvd,LPDWORD lpFlags,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

参数含义与WSASend基本相同,不再赘述。

综合运用实例

#include "stdafx.h" #include <winsock2.h> #include <stdio.h> #include <iostream>  using namespace::std; #define BUF_SIZE 1024  void CALLBACK ReadCompRoutine(DWORD, DWORD, LPWSAOVERLAPPED, DWORD); void CALLBACK WriteCompRoutine(DWORD, DWORD, LPWSAOVERLAPPED, DWORD); void ErrorHanding(char * message);  typedef struct {     SOCKET hClntSock;   //套接字句柄     char buf[BUF_SIZE]; //缓冲     WSABUF wsaBuf;      //缓冲相关信息 }PER_IO_DATA,*LPPER_IO_DATA;   void main() {     WSADATA wsaData;     SOCKET hLisnSock, hRecvSock;     SOCKADDR_IN lisnAdr, recvAdr;     LPWSAOVERLAPPED lpOvlap;     DWORD recvBytes;     LPPER_IO_DATA hbInfo;     int recvAdrSz;     DWORD flagInfo = 0;     u_long mode = 1;      if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//加载库并获取库信息填至wsaData         ErrorHanding("socket start error!");      //参数:协议族,套接字传输方式,使用的协议,WSA_PROTOCOL_INFO结构体地址/不需要时传null,扩展保留参数,套接字属性信息     hLisnSock = WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);     //将hLisnSock句柄的套接字I/O模式(FIONBIO)改为mode中指定的形式:非阻塞模式     ioctlsocket(hLisnSock, FIONBIO, &mode);      //设置目标地址端口     memset(&lisnAdr, 0, sizeof(lisnAdr));     lisnAdr.sin_family = AF_INET;     lisnAdr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");     lisnAdr.sin_port = htons(6000);      //套接字绑定     if (bind(hLisnSock, (SOCKADDR*)&lisnAdr, sizeof(lisnAdr)) == SOCKET_ERROR)         ErrorHanding("socket bind error!");      //设置为监听模式     if (listen(hLisnSock, 5) == SOCKET_ERROR)         ErrorHanding("socket listen error!");      recvAdrSz = sizeof(recvAdr);     while (1)     {         //进入短暂alertable wait 模式,运行ReadCompRoutine、WriteCompRoutine函数         SleepEx(100, TRUE);          //非阻塞套接字,需要处理INVALID_SOCKET         //返回的新的套接字也是非阻塞的         hRecvSock = accept(hLisnSock, (SOCKADDR*)&recvAdr, &recvAdrSz);         if (hRecvSock == INVALID_SOCKET)         {             //无客户端连接时,accept返回INVALID_SOCKET,WSAGetLastError()返回WSAEWOULDBLOCK             if (WSAGetLastError() == WSAEWOULDBLOCK)                 continue;             else                 ErrorHanding("accept() error");         }          puts("Client connected");          //申请重叠I/O需要使用的结构体变量的内存空间并初始化         //在循环内部申请:每个客户端需要独立的WSAOVERLAPPED结构体变量         lpOvlap = (LPWSAOVERLAPPED)malloc(sizeof(WSAOVERLAPPED));         memset(lpOvlap, 0, sizeof(WSAOVERLAPPED));          hbInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));         hbInfo->hClntSock = (DWORD)hRecvSock;          (hbInfo->wsaBuf).buf = hbInfo->buf;         (hbInfo->wsaBuf).len = BUF_SIZE;          //基于CR的重叠I/O不需要事件对象,故可以用来传递其他信息         lpOvlap->hEvent = (HANDLE)hbInfo;         //接收第一条信息         WSARecv(hRecvSock, &(hbInfo->wsaBuf), 1, &recvBytes, &flagInfo, lpOvlap, ReadCompRoutine);     }     closesocket(hRecvSock);     closesocket(hLisnSock);     WSACleanup();     return; } //参数:错误信息,实际收发字节数,OVERLAPPED类型对象,调用I/O函数时传入的特性信息 void CALLBACK ReadCompRoutine(DWORD dwError, DWORD szRecvBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags) {     //从lpoverlapped中恢复传递的信息     LPPER_IO_DATA hbInfo = (LPPER_IO_DATA)(lpOverlapped->hEvent);     SOCKET hSock = hbInfo->hClntSock;     LPWSABUF bufInfo = &(hbInfo->wsaBuf);     DWORD sentBytes;      //接收到EOF,断开连接     if (szRecvBytes == 0)     {         closesocket(hSock);         free(hbInfo);         free(lpOverlapped);         puts("Client disconnected");     }     else     {         bufInfo->len = szRecvBytes;         //将接收到的信息回传回去,传递完毕执行WriteCompRoutine(): 接收信息         WSASend(hSock, bufInfo, 1, &sentBytes, 0, lpOverlapped, WriteCompRoutine);     } }  //参数:错误信息,实际收发字节数,OVERLAPPED类型对象,调用I/O函数时传入的特性信息 void CALLBACK WriteCompRoutine(DWORD dwError, DWORD szRecvBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags) {     //从lpoverlapped中恢复传递的信息     LPPER_IO_DATA hbInfo = (LPPER_IO_DATA)(lpOverlapped->hEvent);     SOCKET hSock = hbInfo->hClntSock;     LPWSABUF bufInfo = &(hbInfo->wsaBuf);     DWORD recvBytes;     DWORD flagInfo = 0;     //接收数据,接收完毕执行ReadCompRoutine:发送数据     WSARecv(hSock, bufInfo, 1, &recvBytes, &flagInfo, lpOverlapped, ReadCompRoutine); }  void ErrorHanding(char * message) {     cout << message << endl;     exit(1); }
文章来源: windows下的IOCP
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!