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

本文探讨如何在重叠I/O完成例程模型中同时处理WSARecv和WSASend操作。通过自定义结构体增加dwBytesSend和dwBytesRecv字段来区分发送和接收状态,当dwBytesRecv大于dwBytesSend时,继续投递WSASend以确保数据完整传输。文章引用了详细的英文资源并提供了线程代码示例。
摘要由CSDN通过智能技术生成

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


参考自这篇文章:
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;
			}
		}
	}
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值