BUG: 错误使用IOCP导致在多PCI-E网卡无法接收到数据

BUG: 错误使用IOCP导致在多PCI-E网卡无法接收到数据

前言

该BUG是出现在实现GigE-Version CTI Discover模块中出现的,为了可以异步获取连接在上位机不同网卡的多台设备信息,我采用的IOCP方式实现的Discover模块。

1、IOCP简易调用图

请添加图片描述

如上图所示,我们会为在多网卡情况下我们会为每个网卡都创建一个Socket对象,并将该Socket绑定到IOCP上和接受操作绑定到IOCP上。

之后当有Client向某个网卡的Socket发送数据时,GetQueuedCompletionStatus便会感知到进行后续操作。

2、问题现象

在只有主板网卡时,程序可以正常接收到连接在主板网卡设备的信息;但是当上位机插入多个PCI-E网卡并将设备(GigE设备)插入到这些PCI-E网卡上时,Discover模块就无法检测到设备的存在了,并且GetQueuedCompletionStatus一直是阻塞状态(说明IOCP根本就没有检测到有ACK包进来)。

这里开始怀疑是否是设备没有收到Discover-cmd或者上位机没有收到设备返回的ACK;使用Wireshark监听发现设备接受到了CMD并发送了ACK,上位机已经接收到了ACK,就是IOCP没有反应。

3、问题代码及问题分析

for(网卡 : 多网卡){
    // socket socket绑定...
    PostIOCPRecvs(discoverSocket, MAX_DISCOVER_BUFFER_SIZE, DEFAULT_PENDING_NUMS); // 问题点2
    // ...
}

void GevDiscoverClient::PostIOCPRecvs(SOCKET discoverSock, const size_t recvBufferSize,
	const size_t pendingRecvs) {
	while (totalBuffersAllocated < pendingRecvs) // 问题点2
	{
		DWORD receiveBuffersAllocated = 0;
		char *pBuffer = AllocateBufferSpace(recvBufferSize, pendingRecvs, receiveBuffersAllocated);
		totalBuffersAllocated += receiveBuffersAllocated;
		DWORD offset = 0;
		const DWORD recvFlags = 0;
		EXTENDED_OVERLAPPED *pBufs = new EXTENDED_OVERLAPPED[receiveBuffersAllocated];
		DWORD bytesRecvd = 0;
		DWORD flags = 0;
		for (DWORD i = 0; i < receiveBuffersAllocated; ++i)
		{
			EXTENDED_OVERLAPPED *pOverlapped = pBufs + i;
			ZeroMemory(pOverlapped, sizeof(EXTENDED_OVERLAPPED));
			pOverlapped->buf.buf = pBuffer + offset;
			pOverlapped->buf.len = recvBufferSize;
			offset += recvBufferSize;
			if (SOCKET_ERROR == ::WSARecvFrom(discoverSock, &(pOverlapped->buf), 1, &bytesRecvd, &flags, NULL, NULL, pOverlapped, 0))
			{
				const DWORD lastError = ::GetLastError();
				if (lastError != ERROR_IO_PENDING)
				{
					ErrorExit("WSARecv", lastError);
				}
			}
		}
		if (totalBuffersAllocated != pendingRecvs)
		{
			std::cout << pendingRecvs << " receives pending" << std::endl;
		}
	};
}

如上代码有两处问题点,一个是调用PostIOCPRecvs地方,一个是PostIOCPRecvs实现的地方;

问题点1在调用的地方多网卡情况下pendingRecvs的值一直没有改变,归根到底还是问题点2的原因;

问题点2中在第一次调用后,totalBuffersAllocated == pendingRecvs了,后续的socket就不会再进来了,导致后续的overlapped和接受操作不会和IOCP对象绑定;

没有绑定接受操作到IOCP,即使有数据进到该网卡IOCP也不会有反应,和我们的现象一致。

4、解决方案

我们在使用IOCP时,一定要确保一下几点:

  • 确保socket对象与IOCP对象绑定
  • 确保socket读写操作与IOCP对象绑定(这里其实是两个一个是读,一个是写)

上述代码只需要微微改动就可以解决

void GevDiscoverClient::PostIOCPRecvs(SOCKET discoverSock, const size_t recvBufferSize,
	const size_t pendingRecvs) {
	//while (totalBuffersAllocated < pendingRecvs) // 问题点2
	do
	{
		// ... ...
		// ... ...
	}while(0);// 这里判断可以详细点,对pendingRecvs进行进一步判断

}
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值