您的位置:首页 > 其它

重叠I/O完成例程模型如何同时投递WSARecv和WSASend

2016-11-28 21:59 369 查看
参考自这篇文章:

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;
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: