基于事件通知的重叠 I/O 模型,是在你投递了一个请求(比如 WSARecv),系统在完成以后是用事件来通知你的,而在完成例程中,系统在网络操作完成以后会自动调用你提供的回调函数
如果你想要使用重叠 I/O 机制带来的高性能模型,又懊恼于基于事件通知的重叠模型要受到 64 个等待事件的限制,还有点畏惧完成端口稍显复杂的初始化过程,那么"完成例程"无疑是你最好的选择!
在事件通知模型中我们使用 WSAWaitForMultipleEvents 和 WSAGetOverlappedResult 来完成事件通知的处理,在完成例程中很明显这两个函数用不到了。
有一个函数我们需要聊一下,WSARecv 函数,上一节我们有个参数设置为 NULL。这就是我们本次要聊的,它的原型如下
void LpwsaoverlappedCompletionRoutine(
DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags
)
字段
说明
dwError
投递的重叠操作,比如 WSARecv,完成的状态是什么
cbTransferred
实际传输的字节总数
lpOverlapped
传递到最初的 IO 调用内的一个重叠 结构
dwFlags
返回操作结束时可能用的标志
此处省略客户端代码了,因为就是无限发送 HelloWorld
服务器端代码是基于事件通知修改的,一直到监听客户端连接都一样,唯一变动的就是回调函数
procedure RecvCompletionRoutine(error, BytesTransferred: DWORD; Overlapped: POverlapped; InFlags: DWORD); stdcall;
var
Index: Integer;
begin
//遍历数组,获取之前的 Socket 和事件及缓冲区
for Index := 0 to WSA_MAXIMUM_WAIT_EVENTS - 1 do begin
if OverlappedDomain.Events[Index] = Overlapped^.hEvent then begin
//IO 已经完成
if (error = 0) and (BytesTransferred > 0) then
Writeln('客户端序号:' + IntToStr(Index) + ',内容:', PChar(wBuf.buf))
else begin
//数据已经不可读取(客户端发生异常)
closesocket(OverlappedDomain.Clients[Index]);
WSACloseEvent(OverlappedDomain.Events[Index]);
EventTotal := EventTotal - 1;
end;
break;
end;
end;
在 无线侦听客户端连接的线程 中,我们需要调整一行代码
//很明显,在最后一个参数指定我们的回调函数
WSARecv(OverlappedDomain.Clients[EventTotal], @wBuf, 1, Cardinal(rec), Cardinal(flg), @OverlappedDomain.Overlappeds[EventTotal], @RecvCompletionRoutine);
国内的博客环境真的一言难尽,我是从第一篇文章中读到的第二篇
昨天把事件通知的文章写完也没详细的审查,里面有些逻辑并没有讲清楚。唉,总不能重新发一遍吧,所以还是直接读代码吧