windows - IOCP C ++ TCP klient

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har problemer med at implementere TCP IOCP-klienten. Jeg har implementeret kqueue på Mac OSX så søgte at gøre noget lignende på Windows, og min forståelse er, at IOCP er den nærmeste ting. Det største problem er, at GetCompetetionStatus aldrig vender tilbage og altid timeouts ud. Jeg antager, at jeg mangler noget, når man laver håndtaget til at overvåge, men ikke sikker på hvad. Det er her, jeg har fået indtil videre:


Min forbindelsesrutine: (Fjern noget fejlhåndtering for klarhed)


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
");
    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
", 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;   


Her er send rutinen: (fjern også nogle fejlhåndtering for klarhed)


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
",WSAGetLastError());
if(!pov)
    printf("waiton server failed. Error: \%d
",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
",r,WSAGetLastError());
if(r != 0)
{
    printf("WSASend failed \%d
",GetLastError());
    printf("Bytes transfered: \%d
",bytes\_recvd);
}
if (WSAGetLastError() == WSA\_IO\_PENDING)
    printf("we are async.
");
CreateIoCompletionPort(port, &overlapped.hEvent,ulKey, 0);

BOOL test = GetQueuedCompletionStatus(port,&bytes\_recvd,&ulKey,&pov,timeout*1000); 

CloseHandle(port);
return true;


}


Enhver indsigt ville blive værdsat.

Bedste reference


Du knytter det samme stik med flere IOCompletionPorts. Jeg er sikker på, at det ikke er gyldigt. I din IOPortConnect-funktion (hvor du skriver), kalder du CreateIOCompletionPort 4 gange i én skudhåndtag.


Mit råd:



  • Opret en enkelt IOC Completion Port (som i sidste ende forbinder du mange stikkontakter med).

  • Opret en pulje af medarbejdertråde (ved at kalde CreateThread), som hver derefter blokerer på IOCompletionPort-håndtaget ved at kalde GetQueuedCompletionStatus i en loop.

  • Opret en eller flere WSA\_OVERLAPPED stikkontakter, og associer hver med IOCompletionPort.

  • Brug WSA-sokkelfunktionerne, der tager OVERLAPPED * til at udløse overlappede operationer.

  • Behandle færdiggørelsen af ​​de udstedte anmodninger, da medarbejdertråden vender tilbage fra GetQueuedCompletionStatus med OVERLAPPED * du indsendte til at starte operationen.



Bemærk: WSASend returnerer både 0 og SOCKET\_ERROR med WSAGetLastError () som WSA\_IO\_PENDING som koder for at indikere, at du vil få en IO Completion Packet, der ankommer til GetQueuedCompletionStatus. Enhver anden fejlkode betyder, at du skal behandle fejlen straks, da en IO-operation ikke var i kø, så der vil ikke blive yderligere tilbagekald.


Note2: OVERLAPPED * videregivet til WSASend (eller hvad) funktionen er OVERLAPPED * returneret fra GetQueuedCompletionStatus. Du kan bruge denne kendsgerning til at videregive flere kontekstoplysninger med opkaldet:


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

Andre referencer 1


Chris har behandlet de fleste af problemerne, og du har sikkert allerede set på mange eksempler på kode, men ...


Jeg har fået en gratis IOCP-kode, der er tilgængelig her: http://www.serverframework.com/products---the-free-framework.html [4]


Der er også flere af mine CodeProject-artikler om emnet knyttet fra denne side.