C++ Windows Socket五种I/O模型之重叠IO模型(一)

重叠模型的基本原理

简单来说,重叠模型就是让应用程序使用重叠数据结构WSAOVERLAPPED,次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。
需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):

1. 事件对象通知(event object notification)
2. 完成例程(completion routines) ,注意,这里并不是完成端口
本篇文章主要讲一下:基于事件通知模型的重叠模型。
完成例程可以看下一章:
C++ Windows Socket五种I/O模型之重叠IO模型(二)

WSAOVERLAPPED结构

typedef struct _WSAOVERLAPPED {
   
    
    DWORD Internal; 
    DWORD InternalHigh; 
    DWORD Offset; 
    DWORD OffsetHigh; 
    WSAEVENT hEvent;// 唯一需要关注的参数,用来关联WSAEvent对象
} WSAOVERLAPPED, *LPWSAOVERLAPPED;

参数:

  1. Internal: 预留给操作系统使用。它指定一个独立于系统的状态,当GetOverlappedResult函数返回时没有设置扩展错误信息ERROR_IO_PENDING时有效。
  2. InternalHigh: 预留给操作系统使用。它指定长度的数据转移,当GetOverlappedResult函数返回TRUE时有效。
  3. Offset: 该文件的位置是从文件起始处的字节偏移量。调用进程设置这个成员之前调用ReadFile或WriteFile函数。当读取或写入命名管道和通信设备时这个成员被忽略设为零。
  4. OffsetHigh: 指定文件传送的 字节 偏移量的高位字。当读取或写入命名管道和通信设备时这个成员被忽略设为零。
  5. hEvent: 在转移完成时处理一个事件设置为有信号状态。调用进程集这个成员在调用ReadFile、 WriteFile、TransactNamedPipe、 ConnectNamedPipe函数之前。

WSARecv
在重叠模型中,接收数据就要靠它了,它的参数也比recv要多,因为要用到重叠结构嘛,它是这样定义的:

int WSARecv(
    SOCKET s,// 当然是投递这个操作的套接字
    LPWSABUF lpBuffers,       //接收缓冲区,与Recv函数不同,需要一个由WSABUF结构构成的数组
    DWORD dwBufferCount,      // 数组中WSABUF结构的数量
    LPDWORD lpNumberOfBytesRecvd,// 如果接收操作立即完成,这里会返回函数调用接收到的字节数
    LPDWORD lpFlags,             // 具体内容比较多,具体可以看MSDN,可以对接收数据进行筛选,我们这里设置为0即可
    LPWSAOVERLAPPED lpOverlapped,// “绑定”的重叠结构
    LPWSAOVERLAPPED_COMPLETION_ROUTINE  lpCompletionRoutine // 完成例程中将会用到的参数,基于事件通知就设置为 NULL
);

返回值:
如果事件成功并且立即完成的情况下,返回 0,否则就返回SOCKET_ERROR ,可以调用WSAGetLastError 函数来获取错误码,如果错误码为:WSA_IO_PENDING 表示重叠操作已经成功,稍后将完成重叠操作,其他情况下标明重叠操作不成功。

WSAWaitForMultipleEvents
等待事件触发函数,函数原型如下

DWORD WSAWaitForMultipleEvents(
DWORD cEvents,//等候事件的总数量
const WSAEVENT* lphEvents,//事件数组的指针
BOOL fWaitAll,//设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回,FALSE则任何一个事件被传信函数都要返回,我们这里设置为FALSE
DWORD dwTimeout,//超时时间,如果超时,函数会返回 WSA_WAIT_TIMEOUT,如果设置为0,函数会立即返回
// 如果设置为WSA_INFINITE只有在某一个事件被传信后才会返回,否则一直处于阻塞状态
BOOL fAlertable //在完成例程中会用到这个参数,这里我们先设置为FALSE
);

返回值:
WSA_WAIT_TIMEOUT :最常见的返回值,我们需要做的就是继续Wait
WSA_WAIT_FAILED : 出现了错误,请检查cEvents和lphEvents两个参数是否有效
如果事件数组中有某一个事件被传信了,函数会返回这个事件的索引值,但是这个索引值需要减去预定义值 WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
注意:

  1. WSAWaitForMultipleEvents函数只能支持由WSA_MAXIMUM_WAIT_EVENTS对象定义的一个最大值,是 64,就是说WSAWaitForMultipleEvents只能等待64个事件,如果想同时等待多于64个事件,就要 创建额外的工作者线程,就不得不去管理一个线程池,这一点就不如下一篇要讲到的完成例程模型了。
  2. dwTimeout 参数在本demo 中不能设置为 WSA_INFINITE,因为如果当一个套接字被投递出去,线程一直在等待回信阻塞状态,当另外一个客户端连接进来,由于线程处于阻塞状态,将出现无法响应的情况出现(这个问题查阅了好多资料才发现问题所在)

WSAGetOverlappedResult

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

如果第三个参数 返回值 lpcbTransfer=0,标明连接已经中断,需要一些数组的清理,下面示例会具体的讲解。

简单的介绍了一下用到的知识点,下面是重叠io的服务端代码:
编译环境 :vs2019


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值