重叠I/O完成例程模型如何同时投递WSARecv和WSASend
2016-11-28 21:59
369 查看
参考自这篇文章:
http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancediomethod5g.html
推荐大家去上面看看,讲得很细致,一步一步都很详细。因为都是英文的,所以坚持吧~
下面重点就解释一下其原理,是通过什么方式来使一个完成例程同时处理WSARecv和WSASend的。
首先我们要改变一下自定义的结构体,如下所示:
多了两个字段,dwBytesSend和dwBytesRecv,这两个很关键。这两个可以区分当前是否要投递WSARecv还是WSASend,如果dwBytesRecv大于dwBytesSend的时候,则为说明数据未发送完成,继续投递WSASend。
线程的代码和上一篇的一样(重叠I/O之完成例程)
下面是完成例程中的代码
http://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancediomethod5g.html
推荐大家去上面看看,讲得很细致,一步一步都很详细。因为都是英文的,所以坚持吧~
下面重点就解释一下其原理,是通过什么方式来使一个完成例程同时处理WSARecv和WSASend的。
首先我们要改变一下自定义的结构体,如下所示:
//自定义一个存放socket信息的结构体,用于完成例程中对OVERLAPPED的转换 typedef struct _SOCKET_INFORMATION { OVERLAPPED Overlapped; //这个字段一定要放在第一个,否则转换的时候,数据的赋值会出错 SOCKET Socket; //后面的字段顺序可打乱并且不限制字段数,也就是说你还可以多定义几个字段 CHAR Buffer[DATA_BUFSIZE]; WSABUF wsaBuf; DWORD dwBytesSend; DWORD dwBytesRecv; } SOCKET_INFORMATION, *LPSOCKET_INFORMATION;
多了两个字段,dwBytesSend和dwBytesRecv,这两个很关键。这两个可以区分当前是否要投递WSARecv还是WSASend,如果dwBytesRecv大于dwBytesSend的时候,则为说明数据未发送完成,继续投递WSASend。
线程的代码和上一篇的一样(重叠I/O之完成例程)
下面是完成例程中的代码
//完成例程 void CALLBACK CompeletRoutine(DWORD dwError, DWORD dwBytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD dwFlags) { DWORD dwSendBytes, dwRecvBytes; DWORD dwFlag; //强制转换为我们自定义的结构,这里就解释了为什么第一个字段要是OVERLAPPED //因为转换后首地址肯定会相同,读取的数据一定会是Overlapped的数据 //所以要先把Overlapped的数据保存下来,接下来内存中的数据再由系统分配到各个字段中 LPSOCKET_INFORMATION pSI = (LPSOCKET_INFORMATION)Overlapped; if (dwError != 0) printf("I/O operation failed with error %d\n", dwError); if (dwBytesTransferred == 0) printf("Closing socket %d\n\n", pSI->Socket); if (dwError != 0 || dwBytesTransferred == 0) { closesocket(pSI->Socket); GlobalFree(pSI); return; } //检查一下dwBytesRecv是否为0,如果等于0,说明WSARecv刚刚调用完成,更新一下dwBytesRecv字段 if (pSI->dwBytesRecv == 0) { pSI->dwBytesRecv = dwBytesTransferred; pSI->dwBytesSend = 0; } else //如果dwBytesRecv不为0,说明WSASend正在调用,因为不确保是否一次性发送完成 { //,所以要更新一下dwBytesSend字段,把发送了的字节加上。 pSI->dwBytesSend += dwBytesTransferred; } //如果接收到的数据大于发送的,说明当前数据还未发送完成,继续调用WSASend发送 if (pSI->dwBytesRecv > pSI->dwBytesSend) { printf("Recv%d:%s\n", pSI->Socket, pSI->wsaBuf.buf); ZeroMemory(&(pSI->Overlapped), sizeof(WSAOVERLAPPED)); pSI->wsaBuf.buf = pSI->Buffer + pSI->dwBytesSend; //SI->dwBytesSend偏移地址,就是接着发送剩下的数据 pSI->wsaBuf.len = pSI->dwBytesRecv - pSI->dwBytesSend; if (WSASend(pSI->Socket, &(pSI->wsaBuf), 1, &dwSendBytes, 0, &(pSI->Overlapped), CompeletRoutine) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { printf("WSASend() failed with error %d\n", WSAGetLastError()); return; } } } else { //如果已经发送完成了,接着投递下一个WSARecv pSI->dwBytesRecv = 0; dwFlag = 0; ZeroMemory(&(pSI->Overlapped), sizeof(WSAOVERLAPPED)); pSI->wsaBuf.len = DATA_BUFSIZE; pSI->wsaBuf.buf = pSI->Buffer; if (WSARecv(pSI->Socket, &(pSI->wsaBuf), 1, &dwRecvBytes, &dwFlag, &(pSI->Overlapped), CompeletRoutine) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); return; } } } }
相关文章推荐
- WinSock IO模型四: 重叠I/O (完成例程)
- 用完成例程(Completion Routine)实现的重叠I/O模型
- 用完成例程(Completion Routine)实现的重叠I/O模型
- 基于完成例程的重叠I/O网络模型
- 重叠模型之完成例程
- 完成例程(重叠模型) , 完成端口, raw socket,ACE
- Socket IO重叠模型(完成例程)
- 用完成例程(Completion Routine)实现的重叠I/O模型
- 完成端口重叠I/O模型的服务器中,如何存储和管理数万个socket句柄—IOCP代码
- 模型设计与实践---(七)重叠IO ,完成例程
- Socket I/O模型之重叠I/O(overlapped I/O)--完成例程方式
- 手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(下) .
- Overlapped I/O模型--完成例程
- 重叠IO之完成例程的实现方法
- SOCKET编程进阶之Overlapped IO完成例程模型
- Overlapped I/O模型--完成例程
- 手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(上)
- 手把手教你玩转网络编程模型之完成例程(Completion Routine)篇(上)
- 转:SOCKET编程进阶之Overlapped I\O完成例程模型
- SOCKET编程进阶之Overlapped I/O完成例程模型