2011-04-19 12 views
6

Tengo problemas para implementar el cliente TCP IOCP. Implementé kqueue en Mac OSX, por lo que estaba buscando hacer algo similar en Windows y tengo entendido que IOCP es lo más parecido. El principal problema es que GetCompetetionStatus nunca regresa y siempre expira. Supongo que me falta algo cuando creo el controlador para monitorear, pero no estoy seguro de qué. Aquí es donde he conseguido hasta ahora:Cliente IOCP C++ TCP

Mi rutina de conexión: (eliminar algunos gastos de envío para mayor claridad de error)

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; 

Aquí es la rutina de envío: (también eliminar algunos gastos de envío para mayor claridad de error)

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; 

}

Cualquier penetración sería apreciada.

+0

¿Realmente verifica el valor de retorno de 'CreateIoCompletionPort'? –

+0

poniendo GetLastError después de que el CreateIoCompletionPort en cada función devuelve un error 87 (parámetro incorrecto) y error 6 (identificador no válido). No estoy seguro de por qué están regresando incorrectos ya que ambos son identificadores válidos (ServerSocket y puerto). – daltoniam

Respuesta

5

Está asociando el mismo socket con múltiples IOCompletionPorts. Estoy seguro de que eso no es válido. En su función IOPortConnect (Donde realiza la escritura), llama a CreateIOCompletionPort 4 veces pasando en controles de una sola toma.

Mi consejo:

  • Crear un solo puerto IOCompletion (que, en última instancia, se asocia con numerosas tomas).
  • Cree un conjunto de subprocesos de trabajo (llamando a CreateThread) que cada uno bloquea en el identificador de IOCompletionPort llamando a GetQueuedCompletionStatus en un bucle.
  • Cree uno o más sockets WSA_OVERLAPPED y asocie cada uno con IOCompletionPort.
  • Utilice las funciones de socket WSA que toman un OVERLAPPED * para desencadenar operaciones superpuestas.
  • Procesa la finalización de las solicitudes emitidas a medida que los subprocesos de trabajo regresan de GetQueuedCompletionStatus con OVERLAPPED * que pasaste para iniciar la operación.

Nota: WSASend vuelve ambos 0, y SOCKET_ERROR con WSAGetLastError() como WSA_IO_PENDING como códigos para indicar que obtendrá un paquete de finalización IO llegar a GetQueuedCompletionStatus. Cualquier otro código de error significa que debe procesar el error inmediatamente ya que una operación IO no se puso en cola, por lo que no habrá más devoluciones de llamada.

Nota 2: El OVERLAPPED * pasado a la función WSASend (o lo que sea) es el OVERLAPPED * devuelto por GetQueuedCompletionStatus. Puede utilizar este hecho para pasar más información de contexto con la llamada:

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

Gracias.Implementaré eso y veré si me pongo a trabajar. Gracias por tu tiempo. – daltoniam

2

Chris se ha ocupado de la mayoría de los problemas y es probable que ya ha mirado un montón de ejemplo de código, pero ...

Tengo un código gratuito IOCP que está disponible aquí: http://www.serverframework.com/products---the-free-framework.html

También hay varios de mis artículos CodeProject sobre el tema vinculado desde esa página.

+0

Gracias por el enlace voy a echar un vistazo. – daltoniam

Cuestiones relacionadas