我在实现TCP IOCP客户端时遇到了一些麻烦。 我已经在Mac OSX上实现了kqueue,所以想在Windows上做类似的事情,我的理解是IOCP是最接近的。 主要问题是GetCompetetionStatus从不返回,总是超时。 我假设我创build要监视的句柄时丢失了一些东西,但不知道是什么。 这是我迄今得到的地方:
我的连接例程:(清除一些error handling)
struct sockaddr_in server; struct hostent *hp; SOCKET sckfd; WSADATA wsaData; int iResult = WSAStartup( MAKEWORD(2,2), &wsaData ); if ((hp = gethostbyname(host)) == NULL) return NULL; WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED) if ((sckfd = WSASocket(AF_INET,SOCK_STREAM,0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { printf("Error at socket(): Socket\n"); WSACleanup(); return NULL; } server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr = *((struct in_addr *)hp->h_addr); memset(&(server.sin_zero), 0, 8); //non zero means non blocking. 0 is blocking. u_long iMode = -1; iResult = ioctlsocket(sckfd, FIONBIO, &iMode); if (iResult != NO_ERROR) printf("ioctlsocket failed with error: %ld\n", iResult); HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0); CreateIoCompletionPort((HANDLE)sckfd, hNewIOCP , ulKey, 0); connect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr)); //WSAConnect(sckfd, (struct sockaddr *)&server, sizeof(struct sockaddr),NULL,NULL,NULL,NULL); return sckfd;
这里是发送例程:(为了清晰起见,也删除一些error handling)
IOPortConnect(int ServerSocket,int timeout,string& data){ char buf[BUFSIZE]; strcpy(buf,data.c_str()); WSABUF buffer = { BUFSIZE,buf }; DWORD bytes_recvd; int r; ULONG_PTR ulKey = 0; OVERLAPPED overlapped; OVERLAPPED* pov = NULL; HANDLE port; HANDLE hNewIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, ulKey, 0); CreateIoCompletionPort((HANDLE)ServerSocket, hNewIOCP , ulKey, 0); BOOL get = GetQueuedCompletionStatus(hNewIOCP,&bytes_recvd,&ulKey,&pov,timeout*1000); if(!get) printf("waiton server failed. Error: %d\n",WSAGetLastError()); if(!pov) printf("waiton server failed. Error: %d\n",WSAGetLastError()); port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long)0, 0); SecureZeroMemory((PVOID) & overlapped, sizeof (WSAOVERLAPPED)); r = WSASend(ServerSocket, &buffer, 1, &bytes_recvd, NULL, &overlapped, NULL); printf("WSA returned: %d WSALastError: %d\n",r,WSAGetLastError()); if(r != 0) { printf("WSASend failed %d\n",GetLastError()); printf("Bytes transfered: %d\n",bytes_recvd); } if (WSAGetLastError() == WSA_IO_PENDING) printf("we are async.\n"); CreateIoCompletionPort(port, &overlapped.hEvent,ulKey, 0); BOOL test = GetQueuedCompletionStatus(port,&bytes_recvd,&ulKey,&pov,timeout*1000); CloseHandle(port); return true;
}
任何有识之士将不胜感激。
您正在将相同的套接字与多个IOCompletionPorts关联。 我相信那是无效的。 在你的IOPortConnect函数中(在你写的地方),你可以调用CreateIOCompletionPort函数四次传入一个句柄。
我的建议:
注:WSASend以WSA_IO_PENDING的形式返回0和SOCKET_ERROR与WSAGetLastError()作为代码,以指示您将获得到达GetQueuedCompletionStatus的IO完成数据包。 任何其他错误代码意味着你应该立即处理错误,因为IO操作没有排队,所以不会有进一步的回调。
注2:传递给WSASend(或其他)函数的OVERLAPPED *是从GetQueuedCompletionStatus返回的OVERLAPPED *。 你可以使用这个事实来传递更多的上下文信息:
struct MYOVERLAPPED { OVERLAPPED ovl; }; MYOVERLAPPED ctx; WSASend(...,&ctx.ovl); ... OVERLAPPED* pov; if(GetQueuedCompletionStatus(...,&pov,...)){ MYOVERLAPPED* pCtx = (MYOVERLAPPED*)pov;
克里斯已经处理了大部分的问题,你可能已经看了很多示例代码,但是…
我有一些免费的IOCP代码可以在这里找到: http : //www.serverframework.com/products—the-free-framework.html
这个页面上还有一些关于这个主题的CodeProject文章。