2011-04-19 193 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