2010-12-03 10 views
2

Hey ... Ich habe einen kleinen Testserver mit I/O Completion Ports und Winsock erstellt. Ich kann erfolgreich eine Socket-Handle mit dem Completion-Port verbinden und zuordnen. dont Aber ich weiß, wie benutzerdefinierte Datenstrukturen in den wroker Faden ...Wie können benutzerdefinierte Daten mithilfe von IOCP an einen Worker-Thread übergeben werden?

Was Ive wurde versucht, so weit passieren eine benutzer Struktur als (ULONG_PTR)&structure as Bestehen der Fertigstellung Key im Verein-Aufruf von CreateIoCompletionPort() Aber das hat nicht funktioniert.

Jetzt habe ich versucht, meine eigene OVERLAPPED-Struktur zu definieren und CONTAINING_RECORD() zu verwenden, wie hier beschrieben http://msdn.microsoft.com/en-us/magazine/cc302334.aspx und http://msdn.microsoft.com/en-us/magazine/bb985148.aspx. Aber das funktioniert auch nicht. (Ich bekomme freaky Werte für den Inhalt von pHelper)

Also meine Frage ist: Wie kann ich Daten übergeben, die Worker-Thread mit WSARecv(), GetQueuedCompletionStatus() und das Completion-Paket oder die OVERLAPPED-Struktur?

EDIT: Wie kann ich "per-connection-data" erfolgreich übertragen? ... Es scheint, als ob ich die Kunst, es zu tun (wie in den beiden obigen Links erklärt) falsch gemacht habe.

geht hier mein Code: (Ja, es ist hässlich und es ist nur TEST-Code)

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); 
} 

Antwort

1

Sie können eigene Sonderdaten über PostQueuedCompletionStatus an den Completion Port senden.

Das I/O-Completion-Paketes wird einen hervorragenden Aufruf der GetQueuedCompletionStatus Funktion erfüllen. Diese Funktion gibt die drei Werte, die als zweiter, dritter, und vierter Parameter des Aufrufs an PostQueuedCompletionStatus übergeben wurden, zurück. Das System verwendet oder validiert diese Werte nicht. Insbesondere muss der lpOverlapped-Parameter nicht auf eine OVERLAPPED-Struktur zeigen.

+0

Hey, Danke für die Hilfe. Können Sie mir auch bei meiner zweiten Frage helfen? Wie kann ich diese "per-connection-data" korrekt über die OVERLAPPED-Struktur senden? ... Wie in meinem OP erwähnt, habe ich es nicht richtig mit dem CONTAINING_RECORD() - Macro – Incubbus

+0

Warum bewegst du den '' nicht OVERLAPPED over; 'Mitglied an die Spitze der' struct' und dann einfach den Zeiger auf 'helper *'? –

+0

Auch wenn es funktioniert, scheint mir das irgendwie falsch - abhängig von der Reihenfolge der Strukturen innerhalb von Strukturen - ... – Incubbus

4

Wenn Sie Ihre Struktur wie dies passieren sollte es gut funktionieren:

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


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

Bearbeiten zum Hinzufügen pro E/A-Daten:

Eine der häufig missbraucht Funktionen der asynchronen Apis ist Sie kopieren die OVERLAPPED-Struktur nicht, sie verwenden einfach die bereitgestellte - daher verweist die überlappende Struktur, die von GetQueuedCompletionStatus zurückgegeben wird, auf die ursprünglich bereitgestellte Struktur. Also:

Beachten Sie, dass Sie in Ihrer ursprünglichen Probe, Ihr Casting falsch war. (OVERLAPPED *) pHelper übergab einen Zeiger an den START der Hilfsstruktur, aber der OVERLAPPED-Teil wurde zuletzt deklariert. Ich änderte es, um die Adresse des tatsächlichen überlappten Teils zu übergeben, was bedeutet, dass der Code ohne eine Besetzung kompiliert wird, die uns wissen lässt machen wir die richtige Sache. Ich habe auch die überlappende Struktur verschoben, um das erste Mitglied der Struktur zu sein.

Um die Daten auf der anderen Seite zu fangen:

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

Auf dieser Seite ist es besonders wichtig, dass die überlappende Struktur das erste Mitglied des Helfer struct ist, so dass es einfach macht, von der werfen zurück OVERLAPPED * die API gibt uns und den Helfer, den wir eigentlich wollen.

+0

Wow ... Ich habe das schon versucht und es hat nicht funktioniert O_o ... Ich denke, ich habe die Dereferenzierung vermasselt ... – Incubbus

+0

es ist tragisch leicht, diese Art von Fehlreferenzierung zu bekommen. Der Compiler, der uns dazu zwingt, die ganze Zeit zu casten, hilft nicht. –

+0

hehe ... und ich bin immer ein Beweis dafür ^^ ... Weißt du, wie man die mit der OVERLAPPED Struktur gesendeten "per-I/O-Call" Daten an den Thread, der zum Beispiel die WSARecv bearbeitet, bekommt ? – Incubbus

0

Ich benutze die Standardbuchse Routinen (socket, Close, bindet anzunehmen, Verbinden ...) zur Erzeugung/Zerstörung und Readfile/Writefile für I/O, wie sie die Verwendung von überlappenden Struktur zu ermöglichen.

Nachdem Ihr Socket akzeptiert oder verbunden wurde, sollten Sie ihn dem von ihm bereitgestellten Sitzungskontext zuordnen. Dann ordnen Sie Ihren Socket einem IOCP zu und stellen ihm (im dritten Parameter) einen Verweis auf den Sitzungskontext zur Verfügung. Die IOCP weiß nicht, was diese Referenz ist, und interessiert sich auch nicht dafür. Der Verweis bezieht sich auf IHRE Verwendung. Wenn Sie einen IOC über GetQueuedCompletionStatus erhalten, wird die Variable, auf die Parameter 3 verweist, mit der Referenz ausgefüllt, sodass Sie sofort den mit dem Socket-Ereignis verknüpften Kontext finden und mit der Bearbeitung des Ereignisses beginnen können. Ich verwende normalerweise eine indizierte Struktur, die (unter anderem) die Socket-Deklaration, die überlappende Struktur sowie andere sitzungsspezifische Daten enthält. Die Referenz, die ich in Parameter 3 an CreateIoCompletionPort übergebe, ist der Index für das Strukturelement, das den Socket enthält.

Sie müssen überprüfen, ob GetQueuedCompletionStatus eine Beendigung oder ein Zeitlimit zurückgegeben hat. Mit einer Zeitüberschreitung können Sie Ihre indizierte Struktur durchlaufen und sehen, ob einer von ihnen eine Zeitüberschreitung oder etwas anderes hat und entsprechende Hausbewahrungsaktionen durchführt.

Die überlappende Struktur muss auch überprüft werden, um sicherzustellen, dass die E/A korrekt ausgeführt wurde.

Die Funktion, die den IOCP bedient, sollte eine separate Multithread-Einheit sein. Verwenden Sie dieselbe Anzahl von Threads, die Sie in Ihrem System haben, oder zumindest nicht mehr, da dadurch Systemressourcen verschwendet werden (Sie haben nicht mehr Ressourcen für die Wartung des Ereignisses als die Anzahl der Kerne in Ihrem System, oder?) .

IOCPs sind wirklich die beste aller Welten (zu gut, um wahr zu sein) und jeder, der "einen Thread pro Socket" oder "Warte auf mehrere Socket-Listen in einer Funktion" sagt, weiß nicht wovon sie reden . Ersteres betont Ihren Scheduler und letzteres ist Polling und Polling ist IMMER extrem verschwenderisch.