Jak wysyłać wielokrotne bajty danych poza pasmem za pomocą Winsock

Pracuj w dowolnym miejscu i na dowolnym urządzeniu dzięki platformie Microsoft 365

Uaktualnij program Microsoft 365, aby pracować w dowolnym miejscu z najnowszymi funkcjami i aktualizacjami.

Zaktualizuj teraz

Podsumowanie

Czasami należy wysłać pilne dane OOB do swoich odbiorców. Odbiorcy to wszyscy użytkownicy lub aplikacje, które otrzymują dane. Dane OOB mają być traktowane jako dane wyższego priorytetu niż wszystkie zwykłe dane, które można wysłać. Jeśli dane OOB, które chcesz wysłać, są w jednym bajcie, możesz użyć funkcji Wybierz , aby wyszukać dane OOB. Aby odczytać dane, można użyć funkcji recv . Jednak w przypadku protokołu TCP (Transmission Control Protocol) blok danych OOB ma zawsze tylko jedno bajt. Dlatego w przypadku wysłania wielu jednobajtowych danych OOB tylko ostatni bajt danych OOB jest pobierany. Pozostałe dane są traktowane jak zwykłe dane. W tym artykule użyto kodu przykładowego do opisania sposobu wysyłania wielobajtowych danych OOB przy użyciu usługi Microsoft Windows Socket (Winsock).

W TYM ZADANIU

WPROWADZENIE

W tym artykule opisano sposób wysyłania wielu bajtów (znaków) danych poza pasmem przy użyciu usługi Winsock. Przykładowe aplikacje są tworzone w programie Microsoft Visual C++. Ponieważ ten mechanizm OOB nie jest obsługiwany bezpośrednio na poziomie programu Microsoft Windows Socket, należy zaimplementować ten mechanizm OOB na poziomie aplikacji. To nie jest prawdziwe dane OOB. W tej aplikacji przykładowej można utworzyć dwa gniazda po stronie klienta, nazywane także nadawcą, w celu wysłania danych OOB i zwykłych danych. Po stronie serwera, nazywane również po stronie odbiornika, można przetwarzać dane z dwóch wątków za pomocą dwóch gniazd. Pojedynczy wątek jest przeznaczony dla danych OOB. Drugi wątek dotyczy zwykłych danych. Aby symulować mechanizm OOB, należy zsynchronizować te dwa wątki. Upewnij się, że wątek przetwarzający dane OOB ma wyższy priorytet niż wątek przetwarzający dane normalne.Uwaga Ta przykładowa aplikacja opisuje sposób wysyłania wielobajtowych danych OOB. Może być konieczna zmiana kodu w celu zapewnienia działania kodu w danym środowisku.powrót do początku

Tworzenie aplikacji klienckiej

  1. Utwórz aplikację kliencką. W tej aplikacji klienckiej w poniższym kodzie przykładowym opisano sposób tworzenia dwóch gniazd:

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

    Zmienna myOOBSocket jest używana do wysyłania danych OOB. Zmienna myNormalSocket jest używana do wysyłania zwykłych danych.

  2. Ponieważ wysyłana zmienna myOOBSocket nie ma prawdziwych danych OOB, trzeba mieć sposób na określenie, jakiego rodzaju dane gniazdo ma wysłać, zanim zaczniesz wysyłać dane. W poniższym kodzie przykładowym opisano sposób wysyłania znaku początkowego w celu powiadomienia serwera o rodzaju danych, które zostaną wysłane. Użyj "U" dla danych OOB i Użyj "N" dla zwykłych danych.

    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. W poniższym kodzie przykładowym opisano, jak ustawić, jak aplikacja kliencka ma monitorować dane wejściowe od użytkownika. Aplikacja kliencka wysyła wielkie litery jako dane OOB. Duplikuj każdy znak wejściowy, aby utworzyć ciąg wielobajtowy.

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

Wyświetlanie kodu przykładowego aplikacji klienckiej, klient. cpp z powrotem na początek

Tworzenie aplikacji serwera

  1. Na stronie serwera Poniższy przykładowy kod opisuje sposób tworzenia gniazda odbiornika w celu monitorowania portu używanego do komunikacji:

    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. W poniższym kodzie przykładowym opisano, jak zadzwonić do funkcji Akceptuj , aby zapewnić, że gniazdo odbiornika oczekuje na próby nawiązania połączenia przychodzącego:

    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. Aby symulować mechanizm OOB, należy zsynchronizować te dwa wątki. W tej przykładowej aplikacji Użyj globalnego obiektu zdarzenia . Umieść następującą instrukcję u góry pliku.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Tworzenie obsługi OOB wykonywanej za pomocą oddzielnego wątku. W ramach tej procedury obsługi funkcja Wybierz umożliwia wykrycie momentu odebrania danych. Po nadejściu danych wykonaj następujące czynności:

    1. Zadzwoń do funkcji ResetEvent , aby ustawić obiekt zdarzenia hRecvEvent na stan bez sygnału.

      ResetEvent(hRecvEvent);

      Spowoduje to zablokowanie normalnej obsługi danych wykonywanej w innym wątku.

    2. Zadzwoń do funkcji revC , aby odczytać dane.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Ponieważ więcej danych może zostać wysłanych, niż może być przechowywana w buforze, należy ponownie zadzwonić do funkcji recv , razem z flagą MSG_PEEK , aby sprawdzić, czy jakieś dane są dostępne w locie.

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

      Użyj jednej z następujących metod w zależności od tego, czy istnieją dane oczekujące na odczytanie lub nie:

      • Jeśli dane nie są oczekujące na odczytanie, zadzwoń do funkcji SetEvent , aby ustawić określony obiekt zdarzenia na sygnał.

        SetEvent(hRecvEvent);

        Po wykonaniu tej czynności można wznowić działanie normalnego wątku obsługi danych. Wątek obsługi danych w normalnym wątku czeka na obiekt zdarzenia hRecvEvent przed wznowieniem normalnego wątku obsługi danych.

      • Jeśli dane nadal oczekują na odczytanie, ponownie Zadzwoń do funkcji recv , aby odczytać pozostałe dane. Następnie ponownie odszukaj dane oczekujące.

    Powtarzaj kroki od 4A do 4C do momentu przeczytania wszystkich oczekujących danych.

  5. Tworzenie normalnej obsługi danych. Ta obsługa jest podobna do procedury obsługi danych OOB, z tą różnicą, że normalna obsługa danych jest wywoływana przez funkcję WaitForSingleObject , gdy normalny moduł obsługi danych wykryje, że dane zostały odebrane.

    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.

Wyświetlanie kodu przykładowego aplikacji serwera,a zlisty . cpp

Wyświetlanie kodu przykładowego aplikacji klienckiej, klient. cpp

Uwaga Aby wykonać kompilację kodu, należy dołączyć odwołanie do pliku biblioteki Winsock Ws2_32. lib.

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

powrót do początku

Wyświetlanie kodu przykładowego aplikacji serwera (nazwa_serwera. cpp)

Uwaga Aby wykonać kompilację kodu, należy dołączyć odwołanie do pliku biblioteki Winsock Ws2_32. lib.

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

powrót do początku

Testowanie próbki

  1. W wierszu polecenia wpisz ciąg nazwa_serwera . exe 4444 , aby uruchomić serwer na porcie 4444. aplikacja serwera wyświetla następujący komunikat, a następnie czeka na klientów:

    Serwer jest gotowy!

  2. W innym oknie wiersza polecenia wpisz ciągWebClient adres_IP 4444 , aby uruchomić klienta. Uwaga Symbol zastępczy IP to symbol zastępczy adresu IP serwera.

  3. Na serwerze zostanie wyświetlony komunikat podobny do następującego:

    Waiting.Waiting*..*Po otrzymaniu poprzedniej wiadomości musisz wpisać aBcDeFgHi na kliencie w ciągu 10 sekund, zanim serwer będzie kontynuował.

Po około 10 sekundach serwer wyświetli następujące informacje:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [Normal]: aaaaaaaaaa [Normal]: cccccccccc [Normalny]: eeeeeeeeee [Normal]: gggggggggg [Normal]: iiiiiiiiiiPowrót do najwyżej

Informacje

Aby uzyskać dodatkowe informacje, odwiedź następującą witrynę Microsoft Developer Network (MSDN) w sieci Web:

Niezależny od protokołu protokółhttp://msdn2.microsoft.com/en-us/library/ms740102.aspxhttp://msdn2.Microsoft.com/en-us/library/ms741394.aspx funkcji Winsock DataAby uzyskać dodatkowe informacje, kliknij następujący numer artykułu w celu wyświetlenia tego artykułu z bazy wiedzy Microsoft Knowledge Base:

331756 Funkcja Ioctlsocket nie może wykryć wbudowanych danych poza pasmemPowrót do najwyżej

Potrzebna dalsza pomoc?

Rozwijaj swoje umiejętności
Poznaj szkolenia
Uzyskuj nowe funkcje w pierwszej kolejności
Dołącz do niejawnych testerów firmy Microsoft

Czy te informacje były pomocne?

Dziękujemy za opinię!

Dziękujemy za opinię! Wygląda na to, że połączenie Cię z jednym z naszych agentów pomocy technicznej pakietu Office może być pomocne.

×