123456

 

IOCP小记

IOCP事实上是一个Tread pool,但是它运行于内核,所以相对于用户态的select效率应该高了一点点。

它的工作过程一般是这样的:

0.CreateIoCompletionPort创建一个完成端口

1.事先开好N个线程,这线程的个数一般为处理器个数的二倍+2,线程的参数传完成端口的句柄

2.服务器有一个listen线程,当它从已完成三次握手队列中取出一个临时accept套接字时

3.accept绑定到0步创建的完成端口

4.accept使用WSARecv读取客户端发送的数据,并放入缓冲区,使用OVERLAPPED开头的自定义数据结构,WSARecv接收客户端的数据,并把数据放入缓冲区,让工作线程(事先开好的N个线程)来取。注意dwFlags通常设为0,否则会出错

5.工作线程通过GetQueuedCompletionStatus查询,从缓冲区取出数据并加以处理

基本代码:

BOOL CIMServerThread::InitServer()
{
	WSADATA wsadata;
	if (0 != WSAStartup(WINSOCK_VERSION, &wsadata))
	{
		return FALSE;
	}

	SYSTEM_INFO syminfo;
	GetSystemInfo(&syminfo);


	// 确定创建多少个IOCP处理线程
	m_dwThreads = syminfo.dwNumberOfProcessors*2;
	m_phThread = new HANDLE[m_dwThreads];

	return TRUE;
}

BOOL CIMServerThread::Run()
{
	InitServer();

   // 创建监听线程
   if (NULL == m_hListenThread)
   {
	   m_hListenThread = CreateThread(NULL, 0, ListenThread, 
		   NULL, 0, NULL);
   }

	return TRUE;
}

DWORD WINAPI CIMServerThread::ListenThread(LPVOID lp)
{
	HANDLE hCompletionPort
    = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if (NULL == hCompletionPort)
	{
		return TRUE;
	}

	// 为每个CPU创建两个工作线程
	for (DWORD i=0; i<m_dwThreads; i++)
	{
		m_phThread[i] = CreateThread(NULL, 0, WorkThread, hCompletionPort, 
			0, NULL);
	}

	// WSASocket可以使用WinSock特有功能,比如重叠IO,用dwflags指定。
	SOCKET listenSock = WSASocket(AF_INET, SOCK_STREAM, 0, 
		NULL, 0, WSA_FLAG_OVERLAPPED);
	if (INVALID_SOCKET == listenSock)
	{
		return TRUE;
	}

	SOCKADDR_IN sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = htonl(INADDR_ANY);

	if (SOCKET_ERROR == bind(listenSock, (sockaddr*)&sin, sizeof(sin)))
	{
		return TRUE;
	}

	if (SOCKET_ERROR == listen(listenSock, 5))
	{
		return TRUE;
	}

	// 接受连接,并分配给完成端口
	SOCKET acceptSock;
	LPPER_HANDLE_DATA PerHandleDate = NULL;
	DWORD RecvBytes = 0;
	DWORD Flags = 0;
	while (TRUE)
	{
		acceptSock = WSAAccept(listenSock, NULL, NULL, NULL, 0);
		if (INVALID_SOCKET == acceptSock)
		{
			return TRUE;
		}

		PerHandleDate = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HEANDLE_DATA));
        if (NULL == PerHandleDate)
        {
			return TRUE;
        }

		// 对临时socket启动突发断线检测
		BOOL bKeepAlive = TRUE;
		int nRet = setsockopt(acceptSock, 
			SOL_SOCKET,
			SO_KEEPALIVE,
			(char*)&bKeepAlive,
			sizeof(bKeepAlive));
		if (SOCKET_ERROR == nRet)
		{
			return TRUE;
		}

		tcp_keepalive alive_in;
		tcp_keepalive alive_out;
		alive_in.keepalivetime = 5000;
		alive_in.keepaliveinterval = 5000;
		alive_in.onoff = TRUE;

		unsigned long ulBytesReturn = 0;
		nRet = WSAIoctl(acceptSock, SIO_KEEPALIVE_VALS,
			&alive_in, sizeof(alive_in),
			&alive_out, sizeof(alive_out),
			&ulBytesReturn, NULL, NULL);
		if (SOCKET_ERROR == nRet)
		{
			return TRUE;
		}

		//
		PerHandleDate->Sock = acceptSock;
		if (NULL == CreateIoCompletionPort((HANDLE)acceptSock, hCompletionPort,
			(ULONG_PTR)PerHandleDate, 0))
		{
			return TRUE;
		}

		LPPER_IO_OPERATION_DATA PerIoData = 
			(LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, 
			sizeof(PER_IO_OPERATION_DATA));
		if (NULL == PerIoData)
		{
			return TRUE;
		}

		memset(&(PerIoData->Overlapped), 0, sizeof(OVERLAPPED));
		PerIoData->DataBuf.len = DATA_BUFSIZE;
		PerIoData->DataBuf.buf = PerIoData->Buffer;
		PerIoData->hWnd = AfxGetApp()->m_pMainWnd->GetSafeHwnd();
		PerIoData->send = 1;

		Flags = 0;
		if (SOCKET_ERROR == WSARecv(acceptSock, &(PerIoData->DataBuf), 1, 
			&RecvBytes, &Flags, &(PerIoData->Overlapped), NULL))
		{

			if (ERROR_IO_PENDING != WSAGetLastError())
			{
				return TRUE;
			}
		}
	}

	return TRUE;
}

DWORD WINAPI CIMServerThread::WorkThread(LPVOID lp)
{
	while (TRUE)
	{
		HANDLE hCompletionPort = (HANDLE)lp;
		DWORD BytesTransferred = 0;
		LPPER_HANDLE_DATA PerHanleData;
		LPPER_IO_OPERATION_DATA PerIoData;
		HWND hWnd = NULL;
		SOCKET socket = INVALID_SOCKET;
		if (0 == GetQueuedCompletionStatus(hCompletionPort,
			&BytesTransferred, 
			(LPDWORD)&PerHanleData,
			(LPOVERLAPPED*)&PerIoData,
			INFINITE))//为0表示失败
		{
			hWnd = PerIoData->hWnd;
		    socket = PerHanleData->Sock;

			FreeAndNull(PerIoData);
			FreeAndNull(PerHanleData);

			// 获取错误代码
			int nLastErr = WSAGetLastError();

			// 错误处理
			switch (nLastErr)
			{
			case ERROR_NETNAME_DELETED:
			case ERROR_OPERATION_ABORTED:
				{
					::SendMessage(hWnd, IM_S_LOGOFF, NULL, socket);
					closesocket(socket);
				}
				break;

			default:// 发生严重错误
				{
					return 0;
				}
				break;
			}

			// 继续工作
			continue;
		}

		// 下面表示成功的从一个完成端口出列一个完成包
		hWnd = PerIoData->hWnd;
		socket = PerHanleData->Sock;

		// 客户正常退出程序,会接收到0字节
		if (0 == BytesTransferred)
		{
			FreeAndNull(PerHanleData);
			FreeAndNull(PerIoData);

			::SendMessage(hWnd, IM_S_LOGOFF, NULL, socket);
			closesocket(socket);

			// 继续工作
			continue;
		}

		// 数据包过大
		if (1024*2<BytesTransferred)
		{
			FreeAndNull(PerHanleData);
			FreeAndNull(PerIoData);

			::SendMessage(hWnd, IM_S_LOGOFF, NULL, socket);
			closesocket(socket);

			// 继续工作
			continue;
		}

        // 开始正确处理数据包 ---------------------------------
		switch (PerIoData->send)
		{

		case 1:
			{

			}
			break;

		case 2:
			{

			}
			break;

		default:
			break;
		}
	}// while(TRUE)


	return TRUE;
}


 

posted on 2012-06-25 13:46  hgy413  阅读(174)  评论(0编辑  收藏  举报

导航