Odoslanie viacerých bajtov z pásma údajov pomocou rozhrania Winsock

Súhrn

Niekedy je potrebné odosielať okamžitú správu mimo kapely (OOB) do prijímačov. Prijímače sú všetci používatelia alebo všetky aplikácie, ktoré prijímajú údaje. Chcete, aby sa tieto OOB údaje považovali za údaje s vyššou prioritou než všetky bežné údaje, ktoré môžete odoslať. Ak sú OOB údaje, ktoré chcete odoslať, jedným bajtom, môžete použiť funkciu Select na vyhľadanie údajov OOB. Na čítanie údajov môžete použiť funkciu recv . V protokole TCP (Transmission Control Protocol) je však blok údajov OOB vždy jeden bajt. Ak preto odosielate OOB údajov s viacerými bajtmi, načíta sa len posledný bajt údajov OOB. Zostávajúce údaje sa spracúvajú ako bežné údaje. V tomto článku sa používa vzorový kód na popis spôsobu odosielania údajov OOB s viacerými bajtmi pomocou rozhrania Microsoft Windows Socket (Winsock).

V TEJTO ÚLOHE

ÚVOD

V tomto článku sa popisuje, ako odosielať viacnásobné bajty (znaky) údajov z pásma pomocou rozhrania Winsock. Vzorové aplikácie sa vytvoria v programe Microsoft Visual C++. Keďže tento OOB mechanizmus nie je priamo podporovaný na úrovni soketu systému Microsoft Windows, tento OOB mechanizmus je nutné implementovať na úrovni aplikácie. Toto nie je pravda OOB údajov. V tejto ukážkovej aplikácii vytvoríte dve zásuvky na strane klienta, známe aj ako strana odosielateľa, na odoslanie údajov OOB a bežných údajov. Na strane servera, ktorá sa označuje aj ako strana prijímača, sa na spracovanie údajov dvoma vláknami používajú dve zásuvky. Jedno vlákno je určené na OOB údajov. Druhé vlákno je určené na normálne údaje. Ak chcete simulovať OOB mechanizmus, musíte synchronizovať tieto dva vlákna. Skontrolujte, či vlákno, ktoré spracúva OOB údaje, má vyššiu prioritu ako vlákno, ktoré spracúva normálne údaje.Poznámka: Táto Ukážková aplikácia popisuje jeden spôsob odosielania údajových OOB s viacerými bajtmi. Možno budete musieť revidovať kód, aby kód fungoval vo vašom prostredí.späť na začiatok

Vytvorenie klientskej aplikácie

  1. Vytvorte klientsku aplikáciu. V tejto klientskej aplikácii obsahuje nasledujúci vzorový kód, ako vytvoriť dve zásuvky:

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

    Premenná myOOBSocket sa používa na odosielanie údajov OOB. Premenná myNormalSocket sa používa na odosielanie normálnych údajov.

  2. Pretože údaje, ktoré myOOBSocket premenná odosiela, nie sú pravdivé OOB údaje, musíte mať spôsob, ako zistiť, aký typ údajov má zásuvka Odoslať skôr než začnete odosielať údaje. Nasledujúci vzorový kód popisuje, ako odoslať počiatočný znak na informovanie servera o tom, aký typ údajov sa odošle. Pre bežné údaje použite "U" pre OOB údaje a použite hodnotu "N".

    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. Nasledujúci vzorový kód popisuje, ako nastaviť, aby klientska aplikácia monitoruje vstup od používateľa. Klientska aplikácia odošle veľké písmená ako OOB údaje. Duplikovať každý vstupný znak na vytvorenie reťazca s viacerými bajtmi.

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

Zobrazenie vzorového kódu pre klientsku aplikáciu, klienta. cpp späť na začiatok

Vytvorenie serverovej aplikácie

  1. Na strane servera nasledujúci vzorový kód popisuje, ako vytvoriť zásuvku poslucháča na sledovanie portu, ktorý sa používa na komunikáciu:

    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. Nasledujúci vzorový kód popisuje, ako zavolať na funkciu prijať , aby sa zásuvka pre poslucháčov počkala na pokusy o prichádzajúce pripojenie:

    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. Ak chcete simulovať OOB mechanizmus, musíte synchronizovať tieto dva vlákna. V tejto ukážkovej aplikácii použite na to tento objekt globálneho podujatia . V hornej časti súboru vložte nasledujúci príkaz.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Vytvorte modul OOB, ktorý sa vykoná oddeleným vláknom. V tejto obsluhe použijete funkciu Select na zistenie, kedy príde k údajom. Po prijatí údajov postupujte podľa týchto krokov:

    1. Ak chcete nastaviť objekt udalosti hRecvEvent na nesignalizáciu, zavoláte na funkciu ResetEvent .

      ResetEvent(hRecvEvent);

      Tým sa zablokuje normálna obsluha údajov, ktorá sa vykoná v inom vlákne.

    2. Ak si chcete prečítať údaje, zatelefonujte na funkciu revc .

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Pretože ďalšie údaje sa môžu odoslať ako medzipamäť môže byť zadržaná, zavoláte opäť funkciu recv spolu s príznakom MSG_PEEK a zistite, či sú na kábli k dispozícii žiadne údaje.

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

      Použite jeden z nasledujúcich postupov v závislosti od toho, či sú údaje čakajúce na čítanie alebo nie:

      • Ak nie sú k dispozícii žiadne údaje na čítanie, zavolaním na funkciu SetEvent nastavte zadaný objekt udalosti do stavu signálu.

        SetEvent(hRecvEvent);

        Keď to urobíte, môže sa obnoviť normálne vlákno popisovača údajov. Vlákno popisovača normálneho spracovania údajov počká na objekt udalosti hRecvEvent pred pokračovaním vlákna normálneho popisovača údajov.

      • Ak údaje ešte nie sú k čítaniu, zavoláte znova funkciu recv a prečítajte si zostávajúce údaje. Potom sa znova pozrite na čakajúce údaje.

    Zopakujte kroky 4a až 4c, kým nebudú prečítané všetky čakajúce údaje.

  5. Vytvorte bežnú obsluhu údajov. Táto obsluha je podobná službe OOB údajov s výnimkou toho, že bežná obsluha údajov zavolá funkciu WaitForSingleObject , keď bežná obslužný program údajov zistí, že údaje boli dodané.

    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.

Zobrazenie vzorového kódu pre serverovú aplikáciu, server. cpp Naspäť na začiatok

Zobrazenie vzorového kódu pre klientsku aplikáciu, klienta. cpp

Poznámka: Ak chcete, aby sa kód zostavil, musíte zahrnúť odkaz na súbor knižnice 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;}

späť na začiatok

Zobrazenie vzorového kódu pre serverovú aplikáciu, server. cpp

Poznámka: Ak chcete, aby sa kód zostavil, musíte zahrnúť odkaz na súbor knižnice 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;}

späť na začiatok

Testovanie vzorky

  1. Do príkazového riadka zadajte príkaz server . exe 4444 , ak chcete spustiť server na porte 4444. serverová aplikácia zobrazí nasledujúce hlásenie a potom počká na klientov:

    Server je pripravený.

  2. V inom okne príkazového riadka zadajte klientaip adresa 4444 , čím spustíte klienta. Poznámka: Zástupná IP adresa je zástupný symbol pre IP adresu servera.

  3. Server zobrazí hlásenie podobné nasledujúcemu:

    Waiting.Waiting*..*Keď dostanete predchádzajúcu správu, musíte do 10 sekúnd pred pokračovaním na serveri zadať aBcDeFgHi na klienta.

Po približne 10 sekundách sa na serveri zobrazí nasledovné:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normálne]: aaaaaaaaaa [normálne]: cccccccccc [normálne]: eeeeeeeeee [normálne]: gggggggggg [normálne]: iiiiiiiiiispäť na začiatok

Odkazy

Ďalšie informácie nájdete na týchto webových lokalitách spoločnosti Microsoft Developer Network (MSDN):

Protokol – nezávislý odhttp://msdn2.Microsoft.com/en-us/library/ms740102.aspxúdajov funkcie Winsockhttp://msdn2.Microsoft.com/en-us/library/ms741394.aspxĎalšie informácie nájdete v článku databázy Microsoft Knowledge Base, ktorý sa zobrazí po kliknutí na nasledovné číslo článku:

331756 Funkcia ioctlsocket nedokáže rozpoznať vnorené údaje mimo pásmaspäť na začiatok

Potrebujete ďalšiu pomoc?

Rozšírte svoje zručnosti
Preskúmať školenie
Buďte medzi prvými, ktorí získajú nové funkcie
Pripojiť k Microsoft insiderov chcú

Považujete poskytnuté informácie za užitočné?

Ďakujem za vaše pripomienky!

Ďakujeme vám za pripomienky. Pravdepodobne vám pomôže, ak vás spojíme s pracovníkom podpory pre Office.

×