Comment transmettre des données définies par l'utilisateur à un thread de travail en utilisant IOCP?

StackOverflow https://stackoverflow.com/questions/4344162

Question

Hé ... Je créé un petit serveur de test à l'aide d'E / S ports d'achèvement et Winsock. Je peux connecter avec succès et associer une poignée de prise avec le port d'achèvement. Mais je sais comment ne pas passer définis par l'utilisateur des structures de données dans le fil de wroker ...

Qu'est-ce que Ive a essayé à ce jour 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 ne fonctionne pas.

Maintenant, j'essayé de définir ma propre structure CHEVAUCHEMENT et en utilisant 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 cela ne fonctionne pas, aussi. (Je reçois des valeurs Freaky pour le contenu de pHelper)

Alors ma question est: Comment puis-je transmettre des données au fil des travailleurs utilisant WSARecv (), GetQueuedCompletionStatus () et le paquet d'achèvement ou la CHEVAUCHEMENT-strucutre

?

EDIT: Comment puis-je transmettre avec succès « par connexion-data » ... Il semble que je suis l'art de le faire (comme expliqué dans les deux liens ci-dessus) faux <. / p>

Ici va 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);
}
Était-ce utile?

La solution

Vous pouvez envoyer des données à des fins spéciales de votre propre au port d'achèvement via PostQueuedCompletionStatus .

  

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

Autres conseils

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 les données par IO:

L'une des fonctions les plus fréquemment victimes de mauvais traitements des apis asynchrones est qu'ils ne sont pas copier le struct CHEVAUCHEMENT, ils utilisent simplement celui fourni - d'où le retour struct chevauchée des points GetQueuedCompletionStatus à l'struct prévu à 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 l'échantillon d'origine, vous avez été l'obtention de votre mauvais casting. (CHEVAUCHEMENT *) pHelper était le passage d'un pointeur vers le début de la struct aide, mais la partie a été déclarée CHEVAUCHEMENT dernier. Je l'ai changé pour passer l'adresse de la partie chevauchée réelle, ce qui signifie que le code compile sans un casting, ce qui nous permet de connaissent nous faisons la bonne chose. Je également déplacé le struct chevauchement pour être le premier membre de la struct.

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 à rejetterait de la CHEVAUCHEMENT * l'api nous donne, et l'aide * nous voulons réellement.

I utiliser les routines de douille standard (socket, closesocket, se lient, d'accepter, de connexion, ...) pour la création / destruction et ReadFile / WriteFile pour I / O, car ils permettent l'utilisation de la structure OVERLAPPED.

Après votre prise a accepté ou connecté vous devez associer au contexte de session, de services. Ensuite, vous associez votre prise à un IOCP et (dans le troisième paramètre) fournir une référence au contexte de la session. Le IOCP ne sait pas ce que cette référence est et ne se soucie pas non plus pour cette question. La référence est pour votre usage de sorte que lorsque vous obtenez un CIO par GetQueuedCompletionStatus la variable pointée par le paramètre 3 sera rempli avec la référence afin que vous trouviez immédiatement le contexte associé à l'événement de prise et peut commencer l'entretien de l'événement. J'utilise habituellement une structure indexée contenant (entre autres) la déclaration de prise, la structure chevauchée ainsi que d'autres données spécifiques de session. La référence je passe à CreateIoCompletionPort dans le paramètre 3 sera l'index de l'élément de structure contenant la douille.

Vous devez vérifier si GetQueuedCompletionStatus retourné une fin ou un délai d'attente. Avec un délai d'attente que vous pouvez exécuter à travers la structure indexée et voir (par exemple) si l'un d'entre eux a expiré ou quelque chose d'autre et de prendre des mesures maison de maintien appropriées.

La structure chevauchée doit également être vérifié pour voir que l'E / S terminé correctement.

La fonction desservant la IOCP devrait être une entité distincte multithread. Utilisez le même nombre de threads que vous avez des noyaux dans votre système, ou du moins pas plus que cela gaspille les ressources du système (vous ne disposez pas plus de ressources pour l'entretien de l'événement que le nombre de cœurs dans votre système, non?) .

IOCPs sont vraiment le meilleur de tous les mondes (trop beau pour être vrai) et tous ceux qui dit « un thread par socket » ou « attente sur la liste sockets multiples en une seule fonction » ne savent pas ce dont ils parlent. L'ancien stress votre planificateur et celui-ci est l'interrogation et l'interrogation est toujours un gaspillage considérable.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top