Zusammenfassung

Manchmal müssen Sie dringende Out-of-Band-Daten (OOB) an Ihre Empfänger senden. Empfänger sind alle Benutzer oder Anwendungen, die die Daten empfangen. Sie möchten, dass diese OOB-Daten als Daten mit höherer Priorität als alle normalen Daten behandelt werden, die Sie möglicherweise senden. Wenn die OOB-Daten, die Sie senden möchten, ein Byte sind, können Sie die Select -Funktion verwenden, um nach OOB-Daten zu suchen. Sie können die Empfangs -Funktion verwenden, um die Daten zu lesen. In TCP (Transmission Control Protocol) ist der OOB-Datenblock allerdings immer ein Byte. Wenn Sie also mehr Byte-OOB-Daten senden, wird nur das letzte Byte der OOB-Daten abgerufen. Die restlichen Daten werden wie normale Daten behandelt. In diesem Artikel wird Beispielcode verwendet, um zu beschreiben, wie mehrstufige OOB-Daten mithilfe von Microsoft Windows Socket (Winsock) gesendet werden.

INHALT

EINFÜHRUNG

In diesem Artikel wird beschrieben, wie Sie mehrere bytes (chars) out-of-Band-Daten mithilfe von Winsock senden. Die Beispielanwendungen werden in Microsoft Visual C++ erstellt. Da dieser OOB-Mechanismus nicht direkt auf der Microsoft Windows-Socketebene unterstützt wird, müssen Sie diesen OOB-Mechanismus auf Anwendungsebene implementieren. Dies ist keine echte OOB-Daten. In dieser Beispielanwendung erstellen Sie auf der Clientseite zwei Sockets, die auch als Absenderseite bezeichnet werden, um OOB-Daten und normale Daten zu senden. Auf der Serverseite, die auch als Empfängerseite bezeichnet wird, verwenden Sie zwei Sockets, um die Daten in zwei Threads zu verarbeiten. Ein Thread ist für OOB-Daten. Der andere Thread ist für normale Daten. Zum Simulieren des OOB-Mechanismus müssen diese beiden Threads synchronisiert werden. Stellen Sie sicher, dass der Thread, der OOB-Daten verarbeitet, eine höhere Priorität als der Thread hat, der normale Daten verarbeitet.Hinweis Diese Beispielanwendung beschreibt eine Möglichkeit zum Senden von OOB-mehr Byte Daten. Möglicherweise müssen Sie den Code überarbeiten, damit der Code in Ihrer Umgebung funktioniert.Zurück zum Anfang

Erstellen einer Clientanwendung

  1. Erstellen Sie eine Clientanwendung. In dieser Clientanwendung wird im folgenden Beispielcode beschrieben, wie Sie zwei Sockets erstellen:

    SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    Die myOOBSocket -Variable wird verwendet, um OOB-Daten zu senden. Die myNormalSocket -Variable wird verwendet, um normale Daten zu senden.

  2. Da es sich bei den Daten, die von der myOOBSocket -Variable gesendet werden, nicht um echte OOB-Daten handelt, müssen Sie dem Server mitteilen, welche Art von Daten der Socket senden soll, bevor Sie mit dem Senden von Daten beginnen. Im folgenden Beispielcode wird beschrieben, wie Sie ein Anfangszeichen senden, um den Server darüber zu informieren, welche Art von Daten gesendet werden. Verwenden Sie "U" für OOB-Daten, und verwenden Sie "N" für normale Daten.

    int nErr, nSize;nErr = connect(myNormalSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));//Look for a potential error here.//"remoteIp" is the remote address.nSize = send(myNormalSocket, "N",1, 0);//Look for a potential error here.nErr = connect(myOOBSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));//Look for a potential error here.nSize = send(myOOBSocket,"U",1, 0);//Look for a potential error here.
  3. Der folgende Beispielcode beschreibt, wie die Clientanwendung die Eingaben des Benutzers überwachen lässt. Die Clientanwendung sendet Großbuchstaben als OOB-Daten. Duplizieren Sie jedes Eingabezeichen, um eine mehr Byte-Zeichenfolge zu verfassen.

    for (;;) {int ch = _getch();_putch( ch );if (ch=='.') {shutdown(myOOBSocket, 2);        shutdown(myNormalSocket,2);break;}        char buf[10];        memset(buf, ch, sizeof(buf));        if (isupper(ch)) {nSize = send(myOOBSocket, buf, sizeof(buf), 0);//Do not use the MSG_OOB flag. Send it as normal data.} else {nSize = send(myNormalSocket, buf, sizeof(buf), 0);}...}

Anzeigen des Beispielcodes für die Clientanwendung "myclient. cpp " zurück zum Anfang

Erstellen einer Serveranwendung

  1. Auf der Serverseite wird im folgenden Beispielcode beschrieben, wie Sie einen Listener-Socket erstellen, um den Port zu überwachen, der für die Kommunikation verwendet wird:

    int nErr;SOCKET myListener = INVALID_SOCKET;myListener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//Look for a potential return error here.nErr = bind(myListener,(SOCKADDR *)&localIp,sizeof(localIp));//Look for a potential return error here.//"localIp" is the local address of the server.nErr = listen(myListener, 5);//Look for a potential return error here.
  2. Der folgende Beispielcode beschreibt, wie die Accept -Funktion aufgerufen wird, damit der Listener-Socket auf eingehende Verbindungsversuche wartet:

    for (;;) {struct sockaddr_in remoteIp;SOCKET remoteSocket = INVALID_SOCKET;int nAddrLen = sizeof(SOCKADDR);remoteSocket = accept(myListener, (SOCKADDR *)(&remoteIp), &nAddrLen);//"remoteIp" is a pointer to a buffer that receives //the address of the connecting entity.BYTE buffer[1+1];int recv_len=recv(remoteSocket,(char *)&buffer,1,0);//This supposes that the client sockets will send//"U" or "N" as the initial character.    buffer[recv_len] = '\0';if(strcmp((char *)buffer,"U")==0)hThread=CreateThread(0,0,OOBHandler,(LPVOID)remoteSocket, 0, &dwThreadId);//Create a new thread to process incoming OOB data. "OOBHandler" is a //pointer to the OOB data handler. else if(strcmp((char *)buffer,"N")==0)hThread=CreateThread(0,0,NormalHandler,(LPVOID)remoteSocket, 0, &dwThreadId);//Create a new thread to process incoming normal data. "NormalHandler" is //a pointer to the normal data handler....}
  3. Zum Simulieren des OOB-Mechanismus müssen diese beiden Threads synchronisiert werden. Verwenden Sie in dieser Beispielanwendung ein globales Ereignis Objekt, um dies zu tun. Setzen Sie die folgende Anweisung oben in der Datei.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Erstellen Sie den OOB-Handler, der von einem separaten Thread ausgeführt wird. In diesem Handler verwenden Sie die Select -Funktion, um zu ermitteln, wann Daten eingehen. Wenn Daten eingehen, führen Sie die folgenden Schritte aus:

    1. Rufen Sie die ResetEvent -Funktion auf, um das hRecvEvent -Ereignisobjekt auf den nicht signalisierten Zustand festzulegen.

      ResetEvent(hRecvEvent);

      Dadurch wird der normale Datenhandler blockiert, der in einem anderen Thread ausgeführt wird.

    2. Rufen Sie die revc -Funktion auf, um die Daten zu lesen.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Da mehr Daten gesendet werden können, als der Puffer enthalten kann, rufen Sie die Empfangs -Funktion zusammen mit dem MSG_PEEK -Flag erneut auf, um zu ermitteln, ob Daten auf dem Draht verfügbar sind.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);

      Verwenden Sie eine der folgenden Methoden, je nachdem, ob Daten zum Lesen vorhanden sind oder nicht:

      • Wenn keine Daten ausstehen, um gelesen zu werden, rufen Sie die SetEvent-Funktion auf , um das angegebene Ereignisobjekt auf den signalisierten Zustand festzulegen.

        SetEvent(hRecvEvent);

        Wenn Sie dies tun, kann der normale Datenhandler-Thread fortgesetzt werden. Der normale Datenhandler-Thread wartet auf das hRecvEvent -Ereignisobjekt, bevor der normale Datenhandler-Thread fortgesetzt wird.

      • Wenn Daten noch ausstehend sind, um gelesen zu werden, rufen Sie die Empfangs -Funktion erneut auf, um die restlichen Daten zu lesen. Suchen Sie dann erneut nach ausstehenden Daten.

    Wiederholen Sie die Schritte 4a bis 4C, bis alle ausstehenden Daten gelesen wurden.

  5. Erstellen Sie den normalen Datenhandler. Dieser Handler ähnelt dem OOB-Datenhandler, mit der Ausnahme, dass der normale Datenhandler die WaitForSingleObject -Funktion aufruft, wenn der normale Datenhandler erkennt, dass Daten eingetroffen sind.

    WaitForSingleObject(hRecvEvent,INFINITE);//You can set the interval parameter to a specific value if you do not want  //the normal data handler to wait for the OOB handler indefinitely when OOB data arrives continuously.

Anzeigen des Beispielcodes für die Serveranwendung, "MyServer. cpp " zurück zum Anfang

Anzeigen des Beispielcodes für die Clientanwendung myclient. cpp

Hinweis Sie müssen einen Verweis auf die Winsock-Bibliotheksdatei Ws2_32. lib einfügen, damit der Code kompiliert wird.

#include <stdio.h>#include <stdlib.h>#include <conio.h>#include <assert.h>#include <winsock.h>main(int argc, char *argv[]){int nErr, nSize;WSADATA wsaData;SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;unsigned short nPort; // Listen for the port number.struct sockaddr_in remoteIp;if (argc!=3){printf("TcpDemoC <RemoteIp> <Port>\n");return 0;}// The Init Socket API.nErr = WSAStartup(MAKEWORD(1,1),&wsaData);assert(nErr==0);nPort = (unsigned short)atol(argv[2]);myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myOOBSocket!=INVALID_SOCKET);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myNormalSocket!=INVALID_SOCKET);// Connect to the remote address.remoteIp.sin_family = AF_INET;remoteIp.sin_port = htons(nPort);;remoteIp.sin_addr.S_un.S_addr = inet_addr(argv[1]);nErr = connect(myOOBSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));if (nErr == SOCKET_ERROR) {printf("Connect failed because of %lX\n",  WSAGetLastError());goto Cleanup;}nSize = send(myOOBSocket,"U",1, 0);if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError());        goto Cleanup;}    nErr = connect(myNormalSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));if (nErr == SOCKET_ERROR) {int error = WSAGetLastError();printf("Connect failed because of %lX\n", error);goto Cleanup;}    nSize = send(myNormalSocket, "N",1, 0);if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError());goto Cleanup;}printf("Read for input:\n");for (;;) {int ch = _getch();_putch( ch );if (ch=='.') {shutdown(myOOBSocket, 2);shutdown(myNormalSocket,2);break;}char buf[10];memset(buf, ch, sizeof(buf));if (isupper(ch)) {nSize = send(myOOBSocket, buf, sizeof(buf), 0);}else{nSize = send(myNormalSocket, buf, sizeof(buf), 0);}if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError());break;}}Sleep(1000);Cleanup:if (myOOBSocket!=INVALID_SOCKET)closesocket(myOOBSocket);if (myNormalSocket!=INVALID_SOCKET)closesocket(myNormalSocket);WSACleanup();return 0;}

Zurück zum Anfang

Anzeigen des Beispielcodes für die Serveranwendung MyServer. cpp

Hinweis Sie müssen einen Verweis auf die Winsock-Bibliotheksdatei Ws2_32. lib einfügen, damit der Code kompiliert wird.

#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <winsock.h>#include <assert.h>// Usage: myserver <port>HANDLE hRecvEvent; DWORD WINAPI OOBHandler(LPVOID lpParam){int nErr, nRecv_len;BYTE buffer[10 + 1];BOOL bClosing=FALSE;fd_set fdread;SOCKET remoteSocket=(SOCKET)lpParam;unsigned long ul=1;int nRet = ioctlsocket(remoteSocket, FIONBIO, &ul);if(SOCKET_ERROR==nRet){printf("Socket() failed: %d\n", WSAGetLastError());return 1;}printf("Waiting");for (int i=0; i<10; i++) {printf(".");Sleep(1000);}printf("The OOB handler is ready!\n");while (!bClosing){//Always clear the set before you call the select method.FD_ZERO(&fdread);//Add sockets to the sets.FD_SET(remoteSocket, &fdread);nErr=select(0,&fdread,0,0,0);if(nErr==SOCKET_ERROR){printf("Select() failed: %d\n",WSAGetLastError());return 1;}if(FD_ISSET(remoteSocket,&fdread)){ResetEvent(hRecvEvent);while(1){nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);                    if(nRecv_len==0){bClosing=TRUE;printf("The connection is closed!\n");break;}buffer[nRecv_len] = '\0';printf("[OOB]: %s\n", buffer);                    nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);if (nRecv_len==SOCKET_ERROR) {if(WSAGetLastError()==WSAEWOULDBLOCK)break;elseprintf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());}}SetEvent(hRecvEvent);} }return 0;}DWORD WINAPI NormalHandler(LPVOID lpParam){int nErr,nRecv_len;BYTE buffer[10 + 1];fd_set fdread;SOCKET remoteSocket=(SOCKET)lpParam;printf("Waiting");for (int i=0; i<10; i++) {        printf("*");Sleep(1000);}printf("Normal handler ready!\n");while(1) {//Always clear the set before you call the select method.FD_ZERO(&fdread);//Add sockets to the sets.FD_SET(remoteSocket, &fdread);nErr=select(0,&fdread,0,0,0);if(nErr==SOCKET_ERROR){printf("Select() failed: %d\n",WSAGetLastError());return 1;}if(FD_ISSET(remoteSocket,&fdread)){WaitForSingleObject(hRecvEvent,INFINITE);nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);            if (nRecv_len==SOCKET_ERROR) {printf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());return 1;}            if(nRecv_len==0){printf("Connection Closed!\n");break;}       buffer[nRecv_len] = '\0';printf("[Normal]: %s\n", buffer);}}return 0;}int main(int argc, char *argv[]){WSADATA wsaData;int nErr;SOCKET myListener = INVALID_SOCKET;struct sockaddr_in localIp;unsigned short nPort; DWORD dwThreadId;HANDLE hThread=NULL;if (argc!=2){printf("MyServer <Port>\n");return 0;}nPort = (unsigned short)atol(argv[1]);nErr = WSAStartup(MAKEWORD(2,0),&wsaData);assert(nErr==0);assert(wsaData.wVersion == MAKEWORD(2,0));myListener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myListener!=INVALID_SOCKET);// Bind the listen socket to any local IP address.localIp.sin_family = AF_INET;localIp.sin_port = htons(nPort);;localIp.sin_addr.S_un.S_addr = INADDR_ANY;nErr = bind(myListener,(SOCKADDR *)&localIp,sizeof(localIp));assert(nErr!=SOCKET_ERROR);nErr = listen(myListener, 5);assert(nErr==0);//Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");if (hRecvEvent == NULL) { printf("CreateEvent failed with error 0x%lx\n", GetLastError());return 1;}printf("The server is ready!\n");for (;;) {struct sockaddr_in remoteIp;SOCKET remoteSocket = INVALID_SOCKET;int nAddrLen = sizeof(SOCKADDR);remoteSocket = accept(myListener, (SOCKADDR *)(&remoteIp), &nAddrLen);if(remoteSocket == INVALID_SOCKET) {int error = WSAGetLastError();printf("Accept() failed. Win32 error is 0x%lx\n", GetLastError());goto Cleanup;} else {printf("Connected from %d.%d.%d.%d:%d\n", remoteIp.sin_addr.S_un.S_un_b.s_b1,remoteIp.sin_addr.S_un.S_un_b.s_b2,remoteIp.sin_addr.S_un.S_un_b.s_b3,remoteIp.sin_addr.S_un.S_un_b.s_b4,ntohs(remoteIp.sin_port));BYTE buffer[1+1];int nRecv_len=recv(remoteSocket,(char *)&buffer,1,0);if (nRecv_len==SOCKET_ERROR) {printf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());return 1;}    buffer[nRecv_len] = '\0';if(strcmp((char *)buffer,"U")==0)hThread=CreateThread(0,0,OOBHandler,(LPVOID)remoteSocket, 0, &dwThreadId);else if(strcmp((char *)buffer,"N")==0)hThread=CreateThread(0,0,NormalHandler,(LPVOID)remoteSocket, 0, &dwThreadId);           if(hThread==0){printf("CreateThread() failed: %d\n",GetLastError());return 1;}CloseHandle(hThread);}}closesocket(myListener);Cleanup:WSACleanup();return 0;}

Zurück zum Anfang

Testen des Beispiels

  1. Geben Sie an der Eingabeaufforderung myserver. exe 4444 ein, um den Server auf Port 4444 zu starten. die Serveranwendung zeigt die folgende Meldung an und wartet dann auf Clients:

    Der Server ist bereit!

  2. Geben Sie in einem anderen Eingabeaufforderungsfenstermyclient IPAddress 4444 ein, um den Client zu starten. Hinweis Die Platzhalter- IPAddress ist ein Platzhalter für die IP-Adresse des Servers.

  3. Auf dem Server wird eine Meldung angezeigt, die der folgenden ähnelt:

    Waiting.Waiting*..*Wenn Sie die vorherige Nachricht erhalten haben, müssen Sie aBcDeFgHi auf dem Client innerhalb von 10 Sekunden eingeben, bevor der Server fortgesetzt wird.

Nach ca. 10 Sekunden zeigt der Server Folgendes an:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: ffffffffff [OOB]: HHHHHHHHHH [Normal]: Aaaaaaaaaa [Normal]: cccccccccc [Normal]: eeeeeeeeee [Normal]: gggggggggg [Normal]: IiiiiiiiiiZurück zum Anfang

Informationsquellen

Weitere Informationen finden Sie auf den folgenden MSDN-Websites (Microsoft Developer Network):

Protokollunabhängige out-of-Band-Datenhttp://msdn2.Microsoft.com/en-US/Library/ms740102.aspxWinsock-Funktionenhttp://msdn2.Microsoft.com/en-US/Library/ms741394.aspxWeitere Informationen finden Sie im folgenden Artikel der Microsoft Knowledge Base:

331756 Die ioctlsocket-Funktion kann keine Inline-out-of-Band-Daten erkennenzurück zum Anfang

Benötigen Sie weitere Hilfe?

Ihre Office-Fähigkeiten erweitern
Schulungen erkunden
Neue Funktionen als Erster erhalten
Microsoft Insider beitreten

War diese Information hilfreich?

Wie zufrieden sind Sie mit der Sprachqualität?
Was hat Ihre Erfahrung beeinflusst?

Vielen Dank für Ihr Feedback!

×