Suvestinė

Kartais į savo imtuvus turite nusiųsti skubius su juosta (OOB) duomenis. Imtuvai yra visi vartotojai arba programos, kurios gauna duomenis. Norite, kad šis OOB duomenys būtų traktuojami kaip aukštesnio prioriteto duomenys nei bet kokie įprasti duomenys, kuriuos galite atsiųsti. Jei OOB duomenys, kuriuos norite išsiųsti, yra vieno baito, naudodami funkciją Select galite ieškoti OOB duomenų. Norėdami skaityti duomenis, galite naudoti funkciją gauti . Tačiau duomenų perdavimo valdymo protokolas (TCP), OOB duomenų blokas visada yra vienas baitas. Todėl, jei siunčiate kelių baitų OOB duomenis, nuskaitomi tik paskutiniai OOB duomenų baitai. Likę duomenys traktuojami kaip įprasti duomenys. Šiame straipsnyje naudojamas pavyzdinis kodas, apibūdinantis, kaip išsiųsti kelių baitų OOB duomenis naudojant "Microsoft Windows" lizdą (Winsock).

ŠIOJE UŽDUOTYJE

ĮŽANGA

Šiame straipsnyje aprašyta, kaip išsiųsti kelių baitų (simbolių) "out-of-Band" duomenis naudojant "Winsock". Pavyzdinės taikomosios programos kuriamos naudojant "Microsoft Visual C++". Kadangi šis OOB mechanizmas nėra tiesiogiai palaikomas "Microsoft Windows" jungties lygyje, turite įdiegti šį OOB mechanizmą taikomosios programos lygiu. Tai nėra tiesa OOB duomenys. Šiame pavyzdžio taikomojoje programoje sukuriate du lizdai kliento pusėje, taip pat žinomas kaip siuntėjo pusėje, norėdami išsiųsti OOB duomenis ir įprastinius duomenis. Serverio pusėje, taip pat žinomoje kaip imtuvo pusėje, galite naudoti du lizdus duomenims dviem gijomis apdoroti. Viena gija yra OOB duomenims. Kita gija yra įprastiniuose duomenyse. Norėdami imituoti OOB mechanizmą, turite sinchronizuoti šias dvi temas. Įsitikinkite, kad gija, apdorojimai OOB duomenys turi aukštesnį prioritetą nei gijos, apdoros įprastinius duomenis.Pastaba Šiame pavyzdyje programa aprašo kelių baitų OOB duomenų siuntimo būdą. Norint, kad kodas veiktų jūsų aplinkoje, gali tekti peržiūrėti kodą.atgal į viršų

Kliento taikomosios programos kūrimas

  1. Kliento taikomosios programos kūrimas. Šiame kliento taikomojoje programoje toliau pateiktas pavyzdžio kodas aprašo, kaip kurti du lizdai:

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

    " Myoobsocket Variable" naudojamas norint išsiųsti OOB duomenis. " Mynormalsocket " kintamasis naudojamas norint išsiųsti įprastinius duomenis.

  2. Kadangi duomenys, kuriuos " Myoobsocket Variable" siunčia, nėra tikrieji OOB duomenys, turite turėti būdą, kaip pranešti serveriui, kokio tipo duomenis lizdas turėtų siųsti prieš pradedant siųsti duomenis. Toliau pateiktas pavyzdinis kodas aprašo, kaip siųsti pradinį simbolį Norint pranešti serveriui apie tai, kokio tipo duomenis bus galima siųsti. Naudokite "U", skirtą OOB duomenims, ir įprastam duomenims naudokite "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. Toliau pateiktas pavyzdinis kodas aprašo, kaip padaryti, kad kliento programa stebėtų vartotojo įvestį. Kliento taikomoji programa siunčia didžiąsias raides kaip OOB duomenis. Dubliuoti kiekvieną įvesties simbolį, kad būtų galima kurti kelių baitų eilutę.

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

Rodyti kliento taikomosios programos pavyzdžio kodą, Myclient. cpp atgal į viršų

Serverio taikomosios programos kūrimas

  1. Serverio pusėje esančiame toliau pateiktame kodo pavyzdyje aprašyta, kaip sukurti klausytojo lizdą, skirtą stebėti prievadą, naudojamą ryšiui palaikyti:

    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. Toliau pateiktas pavyzdinis kodas aprašo, kaip paskambinti funkcijai priimti , kad klausytojo lizdas būtų laukimas, kai gaunamas ryšio bandymas:

    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. Norėdami imituoti OOB mechanizmą, turite sinchronizuoti šias dvi temas. Šiame pavyzdžio taikomojoje programoje naudokite visuotinį įvykio objektą, kad tai galėtumėte atlikti. Failo viršuje padėkite šį sakinį.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Sukurkite OOB apdorojimo programą, kurią vykdo atskira gija. Šioje apdorojimo programoje naudokite funkciją Select , kad nustatytumėte, kada duomenys ateina. Kai duomenys atvyks, atlikite šiuos veiksmus:

    1. Skambinkite į " Resetevent " funkciją norėdami nustatyti, kad Hrecvevent įvykio objektas būtų nepažymėtos būsenos.

      ResetEvent(hRecvEvent);

      Tai blokuoja įprastą duomenų apdorojimo programą, kuri vykdoma kitoje gijoje.

    2. Skambinkite funkcijai revc , kad perskaitytumėte duomenis.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Kadangi gali būti nusiųsti daugiau duomenų nei buferis, galite dar kartą iškviesti funkciją gauti kartu su MSG_PEEK vėliavėle, kad nustatytumėte, ar bet kokie duomenys yra laidai.

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

      Naudokite vieną iš toliau nurodytų būdų, atsižvelgdami į tai, ar yra duomenų, kuriuos galima skaityti, ar ne:

      • Jei nėra duomenų, kuriuos būtų galima perskaityti, paskambinkite funkcija Setevent , kad nustatytumėte nurodytą įvykio objektą.

        SetEvent(hRecvEvent);

        Kai tai padarysite, įprastas duomenų apdorojimo siūlas gali atnaujinti. Įprastas duomenų apdorojimo siūlas laukia Hrecvevent įvykio objekto prieš įprastą duomenų apdorojimo gijos giją.

      • Jei duomenys vis dar yra nepakeičiami, skambinkite į funkciją gauti dar kartą, kad perskaitytumėte likusius duomenis. Tada vėl ieškokite laukiančių duomenų.

    Kartokite 4a – 4c veiksmus, kol bus perskaityti visi laukiantys duomenys.

  5. Sukurkite įprastą duomenų apdorojimo programą. Ši apdorojimo programa panaši į OOB duomenų apdorojimo programą, išskyrus tai, kad įprasta duomenų apdorojimo programa iškviečia funkciją WaitForSingleObject , kai duomenų tvarkytojas aptinka, kad duomenys gauti.

    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.

Rodyti serverio taikomosios programos pavyzdžio kodą, myserver. cpp atgal į viršų

Rodyti kliento taikomosios programos pavyzdžio kodą, Myclient. cpp

Pastaba Norėdami, kad kodas būtų kompiliuojamas, turite įtraukti nuorodą į "Winsock" bibliotekos failą, 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;}

atgal į viršų

Rodyti serverio taikomosios programos pavyzdžio kodą, myserver. cpp

Pastaba Norėdami, kad kodas būtų kompiliuojamas, turite įtraukti nuorodą į "Winsock" bibliotekos failą, 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;}

atgal į viršų

Mėginio bandymas

  1. Komandinėje eilutėje įveskite myserver. exe 4444 , kad paleistumėte serverį "Port" 4444. serverio taikomojoje programoje rodomas šis pranešimas ir laukiama klientų:

    Serveris paruoštas!

  2. Lange kita Komandinė eilutė įveskite "Myclient IPaddress 4444 ", kad paleistumėte klientą. Pastaba Vietos rezervavimo ženklo IPaddress yra serverio vietos rezervavimo ženklas.

  3. Serveris rodo pranešimą, panašų į šį:

    Waiting.Waiting*..*Gavę ankstesnį pranešimą, turite įvesti Abcdefghi klientui per 10 sekundžių iki serverio tęsiasi.

Po maždaug 10 sekundžių serveris rodys šiuos dalykus:

[OOB]: bbbbbbbbbb [OOB]: dddddddddd [OOB]: ffffffffff [OOB]: hhhhhhhhhh [įprastas]: Orinta [įprastas]: cccccccc [įprastas]: Eeeeeeeeee [įprastas]: gggggggggg [įprastas]: IIIIIIIIIIatgal į viršų

Nuorodos

Jei turite papildomos informacijos, apsilankykite šiose "Microsoft" programų kūrėjų tinklo (MSDN) žiniatinklio svetainėse:

"Off-of-Band" duomenųhttp://msdn2.Microsoft.com/en-us/library/ms740102.aspxWinsock funkcijoshttp://msdn2.Microsoft.com/en-us/library/ms741394.aspxJei reikia papildomos informacijos, spustelėkite toliau esantį straipsnio numerį ir peržiūrėkite straipsnį "Microsoft" žinių bazėje:

331756 Funkcija Ioctlsocket negali aptikti įdėtųjų neatliktų duomenųatgal į viršų

Reikia daugiau pagalbos?

Norite daugiau parinkčių?

Sužinokite apie prenumeratos pranašumus, peržiūrėkite mokymo kursus, sužinokite, kaip apsaugoti savo įrenginį ir kt.

Bendruomenės padeda užduoti klausimus ir į juos atsakyti, pateikti atsiliepimų ir išgirsti iš ekspertų, turinčių daug žinių.