2010-12-03 1 views
2

Hey ... J'ai créé un petit serveur de test en utilisant les ports d'achèvement d'E/S et winsock. Je peux connecter et associer avec succès une poignée de socket au port d'achèvement. Mais je ne pas savoir comment passer des structures de données définies par l'utilisateur dans le fil wroker ...Comment passer des données définies par l'utilisateur à un thread de travail en utilisant le protocole IOCP?

Ce qui a essayé à ce jour Ive a été le passage d'une structure utilisateur comme (ULONG_PTR)&structure as l'achèvement clé dans l'association d'appel de CreateIoCompletionPort() Mais cela n'a pas fonctionné.

Maintenant, j'ai essayé de définir ma propre structure OVERLAPPED et d'utiliser CONTAINING_RECORD() comme décrit ici http://msdn.microsoft.com/en-us/magazine/cc302334.aspx et http://msdn.microsoft.com/en-us/magazine/bb985148.aspx. Mais ça ne marche pas aussi. Donc, ma question est: Comment puis-je transmettre des données au thread de travail en utilisant WSARecv(), GetQueuedCompletionStatus() et le paquet Achèvement ou le OVERLAPPED-strucutre?

EDIT: Comment puis-je transmettre avec succès "per-connection-data"? ... Il semble que j'ai eu l'art de le faire (comme expliqué dans les deux liens ci-dessus) faux.

va Voici mon code: (Oui, son laid et son seul code TEST)

struct helper 
    { 
     SOCKET m_sock; 
     unsigned int m_key; 
     OVERLAPPED over; 
    }; 


/////// 

SOCKET newSock = INVALID_SOCKET; 
    WSABUF wsabuffer; 
    char cbuf[250]; 
    wsabuffer.buf = cbuf; 
    wsabuffer.len = 250; 
    DWORD flags, bytesrecvd; 


    while(true) 
    { 
     newSock = accept(AcceptorSock, NULL, NULL); 
     if(newSock == INVALID_SOCKET) 
      ErrorAbort("could not accept a connection"); 

     //associate socket with the CP 
     if(CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, 3,0) != hCompletionPort) 
      ErrorAbort("Wrong port associated with the connection"); 
     else 
      cout << "New Connection made and associated\n"; 

     helper* pHelper = new helper; 
     pHelper->m_key = 3; 
     pHelper->m_sock = newSock; 
     memset(&(pHelper->over), 0, sizeof(OVERLAPPED)); 
     flags = 0; 
     bytesrecvd = 0; 

     if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, (OVERLAPPED*)pHelper, NULL) != 0) 
     { 
      if(WSAGetLastError() != WSA_IO_PENDING) 
       ErrorAbort("WSARecv didnt work"); 
     } 
    } 

    //Cleanup 
    CloseHandle(hCompletionPort); 
    cin.get(); 
    return 0; 
} 

DWORD WINAPI ThreadProc(HANDLE h) 
{ 
    DWORD dwNumberOfBytes = 0; 
    OVERLAPPED* pOver = nullptr; 
    helper* pHelper = nullptr; 
    WSABUF RecvBuf; 
    char cBuffer[250]; 
    RecvBuf.buf = cBuffer; 
    RecvBuf.len = 250; 
    DWORD dwRecvBytes = 0; 
    DWORD dwFlags = 0; 
    ULONG_PTR Key = 0; 

    GetQueuedCompletionStatus(h, &dwNumberOfBytes, &Key, &pOver, INFINITE); 

    //Extract helper 
    pHelper = (helper*)CONTAINING_RECORD(pOver, helper, over); 


    cout << "Received Overlapped item" << endl; 
    if(WSARecv(pHelper->m_sock, &RecvBuf, 1, &dwRecvBytes, &dwFlags, pOver, NULL) != 0) 
     cout << "Could not receive data\n"; 
    else 
     cout << "Data Received: " << RecvBuf.buf << endl; 

    ExitThread(0); 
} 

Répondre

1

Vous pouvez envoyer vos propres données spéciales au port d'achèvement via PostQueuedCompletionStatus.

Le paquet d'achèvement d'E/S satisfera un appel en cours à la fonction GetQueuedCompletionStatus. Cette fonction renvoie les trois valeurs transmises en tant que deuxième, troisième, et quatrième paramètres de l'appel à PostQueuedCompletionStatus. Le système n'utilise pas ou ne valide pas ces valeurs. En particulier, le paramètre lpOverlapped ne doit pas pointer vers une structure OVERLAPPED .

+0

Hey, merci pour l'aide. Pouvez-vous m'aider avec ma deuxième question aussi? Comment puis-je envoyer correctement ce "per-connection-data" sur la structure OVERLAPPED? ... Comme mentionné dans mon OP, je n'ai pas réussi à le faire fonctionner avec le CONTENU_RECORD() - Macro – Incubbus

+0

Pourquoi ne déplacez-vous pas le ' OVERLAPPED sur; 'membre au sommet de la' struct' et juste jeter le pointeur sur 'helper *'? –

+0

Même si cela fonctionne, cela me semble erroné - être dépendant de l'ordre des structures dans les structures - ... – Incubbus

4

Si vous passez votre struct comme cela, il devrait fonctionner parfaitement:

helper* pHelper = new helper; 
CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, (ULONG_PTR)pHelper,0); 
... 


helper* pHelper=NULL; 
GetQueuedCompletionStatus(h, &dwNumberOfBytes, (PULONG_PTR)&pHelper, &pOver, INFINITE); 

Modifier pour ajouter par données d'E/S:

L'une des caractéristiques fréquemment utilisées de l'API asynchrone est ils ne copient pas la structure OVERLAPPED, ils utilisent simplement la structure fournie, d'où la structure superposée renvoyée par les points GetQueuedCompletionStatus à la structure fournie à l'origine. Donc:

struct helper { 
    OVERLAPPED m_over; 
    SOCKET  m_socket; 
    UINT  m_key; 
}; 

if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0) 

Notez que, encore une fois, dans votre échantillon original, vous étiez mal tourné. (OVERLAPPED *) pHelper passait un pointeur sur le START de la structure auxiliaire, mais la partie OVERLAPPED a été déclarée en dernier. Je l'ai changé pour passer l'adresse de la partie chevauchée réelle, ce qui signifie que le code compile sans une distribution, ce qui nous permet savoir nous faisons la bonne chose. J'ai également déplacé la structure chevauchée pour être le premier membre de la structure.

Pour attraper les données de l'autre côté:

OVERLAPPED* pOver; 
ULONG_PTR key; 
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE)) 
{ 
    // c cast 
    helper* pConnData = (helper*)pOver; 

De ce côté, il est particulièrement important que la struct chevauchée est le premier membre de la struct aide, comme qui le rend facile à jeter en arrière de la CHEVAUCHEMENT * l'api nous donne, et l'aide * que nous voulons réellement.

+0

Wow ... Je l'ai déjà essayé et ça n'a pas fonctionné O_o ... Je pense que j'ai foiré le déréférencement ... – Incubbus

+0

il est tragiquement facile d'obtenir ce genre de dé-référencement mal. Le compilateur nous oblige à lancer tout le temps n'aide pas. –

+0

hehe ... et j'en suis toujours un preuve ^^ ... Savez-vous comment obtenir les données "per-I/O-call" envoyées avec la structure OVERLAPPED au thread qui gère le WSARecv par exemple ? – Incubbus

0

j'utiliser les routines de socket standards (socket, closesocket, se lient, accepter la connexion ...) pour la création/destruction et ReadFile/WriteFile pour I/O, car ils permettent l'utilisation de la structure OVERLAPPED. Une fois votre socket acceptée ou connectée, vous devez l'associer au contexte de la session qu'il dessert.

Ensuite, vous associez votre socket à un IOCP et (dans le troisième paramètre) lui fournissez une référence au contexte de la session. L'IOCP ne sait pas ce qu'est cette référence et ne s'en soucie pas non plus. La référence est pour VOTRE utilisation de sorte que lorsque vous obtenez un IOC à l'aide de GetQueuedCompletionStatus, la variable pointée par le paramètre 3 sera remplie avec la référence afin que vous trouviez immédiatement le contexte associé à l'événement socket et que vous puissiez commencer l'entretien. J'utilise généralement une structure indexée contenant (entre autres) la déclaration de socket, la structure superposée ainsi que d'autres données spécifiques à la session. La référence que je passe à CreateIoCompletionPort dans le paramètre 3 sera l'index du membre de structure contenant le socket.

Vous devez vérifier si GetQueuedCompletionStatus a renvoyé un achèvement ou un délai d'expiration. Avec un timeout, vous pouvez parcourir votre structure indexée et voir (par exemple) si l'un d'entre eux a expiré ou quelque chose d'autre et prendre les mesures d'entretien appropriées.

La structure en chevauchement doit également être vérifiée pour s'assurer que les E/S ont été complétées correctement.

La fonction desservant le protocole IOCP doit être une entité séparée à plusieurs threads. Utilisez le même nombre de threads que vous avez de cœurs dans votre système, ou au moins pas plus que cela car il gaspille les ressources système (vous n'avez plus de ressources pour gérer l'événement que le nombre de cœurs dans votre système, non?) .

Les IOCPs sont vraiment les meilleurs de tous les mondes (trop beau pour être vrai) et ceux qui disent "un thread par socket" ou "attendent sur une liste multiple socket dans une fonction" ne savent pas de quoi ils parlent . Le premier met l'accent sur votre planificateur et celui-ci interroge et pollue TOUJOURS extrêmement gaspillage.

Questions connexes