IOCP UDP 问题求解?

shenyi0106 2014-12-05 10:37:46
最近在弄一个UDP服务器,用的是IOCP框架(非ASIO,ACE等第三方组件),遇到了一些问题,望各位大侠指点一二:
问题描述如下:

出错点:GetQueuedCompletionStatus直接崩溃,报ntdll.dll!0x776d15ee()错误。
问题描述:
在Debug模式下,最大压力1500Mbps,150000pps,CPU占用60%,程序运行正常,没有崩溃。
在Release模式下,不管怎么调整都会崩溃。

下面贴上部分源代码,望高手指点。
...全文
274 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2014-12-05
  • 打赏
  • 举报
回复
引用 6 楼 shenyi0106 的回复:
万分感谢,没想到就是这个问题!
you‘re welcome .
shenyi0106 2014-12-05
  • 打赏
  • 举报
回复
万分感谢,没想到就是这个问题!
「已注销」 2014-12-05
  • 打赏
  • 举报
回复
完成端口,没有任何问题,就算有,也不是简单写个udp收发就能复现出来的,不要太小看微软的系统软件工程师了。
「已注销」 2014-12-05
  • 打赏
  • 举报
回复
问题还是出在发射上, DWORD dwFlags = 0; if(WSARecvFrom(Handle(), &(PerIoData->DataBuf), 1, (LPDWORD)&(PerIoData->Length), &dwFlags, (PSOCKADDR)(&(PerIoData->Address)), &addrLen, &(PerIoData->Overlapped), NULL) == SOCKET_ERROR) dwFlags是局部变量,你把局部变量的指针交给完成端口,让它完成时向这个局部变量里写入,当然会导致内核异常。
shenyi0106 2014-12-05
  • 打赏
  • 举报
回复
关键数据结构

typedef struct
{
   OVERLAPPED   Overlapped;
   WSABUF       DataBuf;
   CHAR         Buffer[MAX_PACKET_SIZE];
   SOCKADDR_IN  Address;
   //用于发送数据时,分为两部分:高两个字节表示要发送的数据总长度,低两个字节表示已经发送的数据长度
   //用于接收数据,表示接收到数据的总长度
   UINT         Length;
   IoType       Type;
} PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;

typedef struct
{
   SOCKET hSocket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

//会话控制结构
typedef struct 
{
    PER_IO_OPERATION_DATA   PerIoData;
    PER_HANDLE_DATA         PerHandleData;
    SOCKADDR_IN             HostAddr;
	UINT64                  LastTime;
	PVOID                   ExtDataPtr;
	Socket                 *ThisPtr;    //所有者对象指针(这个结构所在类的对象指针)
	Socket                 *SuperPtr;   //管理者对象指针(管理ThisPtr对象的对象指针)
} SESSION, *LPSESSION;
shenyi0106 2014-12-05
  • 打赏
  • 举报
回复
网上也找了些资料,有说UDP不适合IOCP,也有遇到和我一样问题的,但是都没有提供解决方案。 从ASIO等第三方组件中看,他们的UDP运行良好,所以,应该不是系统问题,可能我忽略掉了某些致命处理,导致的上述问题。 希望这方面的高手,能指点出来。 备注:像什么没关联IOCP句柄,投递READ错误等类似的回答就不要了。谢谢各位。
shenyi0106 2014-12-05
  • 打赏
  • 举报
回复

RetCode UDPSocketImpl::PostRead(SESSION *Session)
{
	INT addrLen = sizeof(SOCKADDR_IN);
	LPPER_IO_OPERATION_DATA PerIoData = &(Session->PerIoData);

	if (IsInit() == FALSE)
	{
		return RET_ENOINIT;
	}

	ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
	PerIoData->Type = IoReadFrom;
	PerIoData->DataBuf.len = MAX_PACKET_SIZE;
	PerIoData->DataBuf.buf = PerIoData->Buffer;

	DWORD dwFlags = 0;
	if(WSARecvFrom(Handle(), &(PerIoData->DataBuf), 1, (LPDWORD)&(PerIoData->Length), &dwFlags, 
		(PSOCKADDR)(&(PerIoData->Address)), &addrLen, &(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
	{
		if (WSAGetLastError() != ERROR_IO_PENDING)
		{
			LOGE(this->Logger(), "[%s] WSARecvFrom Filed with ErrorCode=%d", __FUNCTION__, WSAGetLastError());
			return RET_ECALLAPI;
		}
	}
	return RET_ESUC;
}

//工作线程
UINT WINAPI WorkerThread(LPVOID lParam)
{
	HANDLE hCompletionPort = (HANDLE)lParam;
	ASSERT(hCompletionPort != NULL);

	CoInitialize(NULL);
	while(bWorkState)
	{
		DWORD dwBytesTransferred = 0;
		LPPER_HANDLE_DATA PerHandleData = NULL;
	    LPPER_IO_OPERATION_DATA PerIoData = NULL;

		/************************************************************************
		* 查询完成端口状态,如果有数据(发送或接受),则将数据填装在PerIOData   *
		* 中,然后将所对应的Socket句柄填装在PerhandleData,将数据大小填装在     *
		* BytesTransFerred中                                                   *
		************************************************************************/
		if (GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred,(PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE) == FALSE)
		{
			//错误处理,如果返回TRUE,则表示错误处理完成,FALSE表示错误没有得到处理,需要退出线程
			if (!PerIoData || ErrorProcess(WSAGetLastError(), PerIoData)) 
			{
				continue;
			}
			
			//退出工作线程
			break;
		}

		//退出特例检查处理
		if(!PerIoData || ExceptProcess(PerIoData, dwBytesTransferred)) 
		{
			continue;
		}

		//分拣处理读写操作
		switch(PerIoData->Type)
		{
		case IoRead:
			{
				ReadComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		case IoReadFrom:
			{
				ReadFromComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		case IoWrite:
			{
			    WriteComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		case IoWriteTo:
			{
				WriteToComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		case IoAccept:
			{
                AcceptComplete(PerIoData, dwBytesTransferred); 
				break;
			}
		case IoDisConnect:
			{
                CloseComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		case IoConnect:
			{
				ConnectComplete(PerIoData, dwBytesTransferred); 
				break;
			}

		default:break;
		}
	}
	CoUninitialize();

	return 1;
}

static inline VOID ReadFromComplete(LPPER_IO_OPERATION_DATA PerIoData, DWORD dwBytesTransferred)
{
    //获得连接数据
	LPSESSION Session = (LPSESSION)PerIoData;
	//作用对象指针检查
	ASSERT(Session->ThisPtr != NULL);

	LOGW(Session->ThisPtr->Logger(), "[%s] PerIoData->Type=%d, dwBytesTransferred=%ld", __FUNCTION__, PerIoData->Type, dwBytesTransferred);
    
	//处理数据
	PacketProcess(PerIoData, dwBytesTransferred);

    //继续投递读操作
	if (((UDPSocketImpl*)(Session->ThisPtr))->PostRead(Session) != RET_ESUC)
	{
		CloseProcess(PerIoData);
		return;
	}
}

//错误处理
static inline BOOL ErrorProcess(DWORD dwErr, LPPER_IO_OPERATION_DATA PerIoData)
{
	BOOL bRet = FALSE;

	//如果是客户端异常断开,则删除所有对应的结构
	/*****************************************************************************
	*  ERROR_NETNAME_DELETED      指定的网络名不再可用                           *
	*  ERROR_INVALID_PARAMETER    参数不正确                                     *
	*  ERROR_UNEXP_NET_ERR        出现了意外的网络错误                           *
	*  ERROR_CONNECTION_ABORTED   由本地系统终止网络连接                         *
	*  ERROR_OPERATION_ABORTED    由于线程退出或应用程序请求,已放弃 I/O 操作    *
	*  ERROR_CONNECTION_REFUSED   连接被拒绝                                     *
	*  ERROR_PORT_UNREACHABLE     目标端口不可达(网卡发生丢包时产生的)           *
	*  ERROR_NOACCESS             访问地址错误                                   *     
	******************************************************************************/
	//
	//如果IOCP环境已经退出,则不再处理后续错误,直接让工作线程返回 -- by yshen on 2014/02/01
	//关闭客户端,需要将内存返回给内存池,并重用socket,所以必须通过DisconnectEx来关闭socket
	//
	if (bWorkState && HASH_GETBIT(hashbit, dwErr) > 0)
	{
		Socket *pSocket = ((LPSESSION)PerIoData)->ThisPtr;
        ASSERT(pSocket != NULL);

		LOGW(pSocket->Logger(), "entry [%s] type=%d, socket=%d, Err=%d", __FUNCTION__, PerIoData->Type, pSocket->Handle(), dwErr);

		if (pSocket->SocketListener() != NULL)
		{
			pSocket->SocketListener()->OnError(pSocket, (SocketOption)PerIoData->Type, dwErr);
		}

		CloseProcess(PerIoData);
		return TRUE;
	}
	return FALSE;
}
static inline BOOL ExceptProcess(LPPER_IO_OPERATION_DATA PerIoData, DWORD dwBytesTransferred)
{
	// 客户端正常关闭时
	if (bWorkState && 
		dwBytesTransferred == 0 && 
		PerIoData->Type != IoAccept && 
		PerIoData->Type != IoDisConnect && 
		PerIoData->Type != IoConnect)
	{
		CloseProcess(PerIoData);
		return TRUE;
	}
	return FALSE;
}

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧