2011-04-19 186 views
6

我在实现TCP IOCP客户端时遇到了一些麻烦。我在Mac OSX上实现了kqueue,所以希望在Windows上做类似的事情,我的理解是IOCP是最接近的。主要问题是GetCompetetionStatus从不返回并总是超时。我假设我在创建要监视的句柄时缺少一些东西,但不知道是什么。这是我迄今为止得到:IOCP C++ TCP客户端

我的连接程序:(删除一些错误处理的清晰度)

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; 

这里是发送例程:(也去掉了一些错误处理的清晰度)

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; 

}

任何有识之士将不胜感激。

+0

你真的检查'CreateIoCompletionPort'的返回值吗? – 2011-04-19 14:57:02

+0

在每个函数中的CreateIoCompletionPort都返回错误87(不正确的参数)和错误6(无效句柄)之后,将GetLastError置于其后。我不知道他们为什么返回不正确,因为他们都是有效的句柄(ServerSocket和端口)。 – daltoniam 2011-04-19 15:41:16

回答

5

您正在将相同的套接字与多个IOCompletionPorts关联。我相信那是无效的。在你的IOPortConnect函数中(在你写的地方),你调用CreateIOCompletionPort 4次传入一个句柄。

我的建议是:

  • 创建一个单一的IoCompletion端口(即,最终,你无数的插座与关联)。
  • 创建一个工作线程池(通过调用CreateThread),然后每个线程通过在循环中调用GetQueuedCompletionStatus来阻塞IOCompletionPort句柄。
  • 创建一个或多个WSA_OVERLAPPED套接字,并将每个套接字与IOCompletionPort关联。
  • 使用带有OVERLAPPED *的WSA套接字函数来触发重叠操作。
  • 处理已发出请求的完成,因为工作线程从GetQueuedCompletionStatus返回,并使用传入的OVERLAPPED *开始操作。

注:WSASend同时返回0,并与WSAGetLastError SOCKET_ERROR()作为WSA_IO_PENDING作为代码来表明你将得到一个IO完成数据包在到达GetQueuedCompletionStatus时。任何其他的错误代码意味着你应该立即处理错误,因为IO操作没有排队,所以不会有进一步的回调。注2:传递给WSASend(或其他)函数的OVERLAPPED *是从GetQueuedCompletionStatus返回的OVERLAPPED *。你可以利用这一点来与呼叫传递更多背景信息:

struct MYOVERLAPPED { 
    OVERLAPPED ovl; 
}; 
MYOVERLAPPED ctx; 
WSASend(...,&ctx.ovl); 
... 
OVERLAPPED* pov; 
if(GetQueuedCompletionStatus(...,&pov,...)){ 
    MYOVERLAPPED* pCtx = (MYOVERLAPPED*)pov; 
+0

谢谢。我会实现这一点,看看我是否工作。谢谢你的时间。 – daltoniam 2011-04-19 21:28:56