Kā nosūtīt vairāku baitu ārpus diapazona datus, izmantojot Winsock

Kopsavilkums

Dažreiz adresātiem ir jānosūta steidzamas ārpus biroja (OOB) dati. Uztvērēji ir visi lietotāji vai lietojumprogrammas, kas saņem datus. Jūs vēlaties, lai šie OOB dati tiktu apstrādāti kā augstākas prioritātes dati nekā visiem parastajiem datiem, ko varat nosūtīt. Ja OOB dati ir viens baits, varat izmantot funkciju Select , lai atrastu OOB datus. Varat izmantot funkciju Recv , lai lasītu datus. Taču pārraides vadības protokola (TCP) OOB datu blokā vienmēr ir viens baits. Tāpēc, ja sūtāt vairāku baitu OOB datus, tiek izgūts tikai pēdējais OOB datu baits. Pārējie dati tiek apstrādāti kā normāli dati. Šajā rakstā ir izmantots koda paraugs, kas apraksta, kā nosūtīt vairāku baitu OOB datus, izmantojot Microsoft Windows socket (Winsock).

ŠAJĀ UZDEVUMĀ

IEVADS

Šajā rakstā ir paskaidrots, kā nosūtīt vairākus baitus (chars) ārpus diapazona datus, izmantojot Winsock. Lietojumprogrammu paraugi ir izveidoti programmā Microsoft Visual C++. Tā kā šis OOB mehānisms netiek tieši atbalstīts Microsoft Windows socket Level, ir jāievieš šis OOB mehānisms lietojumprogrammas līmenī. Šis nav patiess OOB dati. Šajā lietojumprogrammas paraugā varat izveidot divas rozetes klienta pusē, ko dēvē arī par sūtītāju pusi, lai nosūtītu OOB datus un parastus datus. Servera pusē, kas tiek dēvēta arī par saņēmēja pusi, jūs izmantojat divas ligzdas, lai apstrādātu datus divos pavedienos. Viens pavediens ir paredzēts OOB datiem. Otrs pavediens ir paredzēts parastajiem datiem. Lai simulētu OOB mehānismu, ir jāsinhronizē šos divus pavedienus. Pārliecinieties, vai pavedienam, kas apstrādā OOB datus, ir augstāka prioritāte nekā pavedienam, kas apstrādā parastus datus.Piezīme. Šajā lietojumprogrammas paraugā ir aprakstīts viens veids, kā nosūtīt vairāku baitu OOB datus. Iespējams, jums būs jāmaina kods, lai padarītu kodu darbojamies savā vidē.atpakaļ uz sākumu

Klienta lietojumprogrammas izveide

  1. Klienta lietojumprogrammas izveide. Šajā klienta lietojumprogrammā tālāk sniegtajā piemērā ir aprakstīts, kā izveidot divas rozetes:

    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 mainīgais tiek izmantots, lai nosūtītu OOB datus. MyNormalSocket mainīgais tiek izmantots, lai sūtītu parastus datus.

  2. Tā kā dati, kas tiek sūtīti myOOBSocket mainīgajā, nav patiesi OOB dati, jums ir jābūt iespējai norādīt serverim, kāda veida datus ligzdai vēlaties nosūtīt, pirms sākat datu sūtīšanu. Tālāk sniegtajā piemērā ir aprakstīts, kā nosūtīt sākotnējo rakstzīmi, lai paziņotu serverim par to, kāda veida dati tiks sūtīti. Izmantojiet "U" OOB datiem un izmantojiet "N" parastajiem datiem.

    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. Tālāk sniegtajā piemērā ir aprakstīts, kā padarīt klienta lietojumprogrammu pārraudzīt lietotāja ievadīto informāciju. Klienta lietojumprogramma sūta lielos burtus kā OOB datus. Dublējiet visas ievades rakstzīmes, lai izveidotu vairāku baitu virkni.

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

Rādiet klienta lietojumprogrammas koda paraugu, Myclient. CPP atpakaļ uz sākumu

Servera lietojumprogrammas izveide

  1. Servera pusē šajā piemērā ir aprakstīts, kā izveidot klausītāju ligzdu, lai pārraudzītu saziņai izmantoto portu.

    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. Tālāk sniegtajā piemērā ir aprakstīts, kā piezvanīt funkcijai pieņemt , lai klausītāju ligzdai gaidītu ienākošā savienojuma mēģinājumus:

    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. Lai simulētu OOB mehānismu, ir jāsinhronizē šos divus pavedienus. Šajā lietojumprogrammas paraugā izmantojiet globālā notikuma objektu, lai to izdarītu. Ievietojiet šādu priekšrakstu faila augšdaļā.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Izveidojiet OOB apdarinātāju, ko izpilda atsevišķs pavediens. Šajā apdarinātājā izmantojiet funkciju Select , lai noteiktu, kad tiek saņemti dati. Kad dati tiek saņemti, veiciet tālāk norādītās darbības.

    1. Zvanīšana uz funkciju ResetEvent , lai iestatītu hRecvEvent notikuma objektu uz nesignalizēto stāvokli.

      ResetEvent(hRecvEvent);

      Tādējādi tiek bloķēts parasts datu apdarinātājs, kas tiek izpildīts citā pavedienā.

    2. Lai lasītu datus, sazinieties ar funkciju revc .

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Tā kā var tikt nosūtīti papildu dati nekā buferī, sazinieties ar Recv funkciju kopā ar MSG_PEEK karogu, lai noteiktu, vai uz vadiem ir pieejami dati.

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

      Izmantojiet kādu no tālāk norādītajām metodēm atkarībā no tā, vai ir dati, kas gaida, lai tos lasītu vai nelasītu.

      • Ja nekādi dati netiek gaidīti, sazinieties ar funkciju SetEvent , lai iestatītu norādīto notikuma objektu uz signalizēšanas stāvokli.

        SetEvent(hRecvEvent);

        Veicot šo darbību, var atsākt parasta datu apdarinātāja pavedienu. Parasta datu apdarinātāja pavediens gaida hRecvEvent notikuma objektu pirms parasta datu apdarinātāja pavediena atsākšanas.

      • Ja joprojām tiek gaidīta datu lasīšana, vēlreiz izsauciet funkciju Recv , lai lasītu pārējos datus. Pēc tam vēlreiz meklējiet gaidošos datus.

    Atkārtojiet ar 4a līdz 4C darbībām, līdz visi gaidošie dati ir lasīti.

  5. Izveidojiet parasto datu apdarinātāju. Šis apdarinātājs ir līdzīgs OOB datu apdarinātājam, izņemot to, ka parasts datu apdarinātājs zvana uz funkciju WaitForSingleObject , ja parastais datu apdarinātājs nosaka, ka dati ir saņemti.

    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.

Rādiet servera lietojumprogrammas koda paraugu, myserver. CPP atpakaļ uz sākumu

Rādīt klienta lietojumprogrammas koda paraugu, Myclient. CPP

Piezīme. Ir jāiekļauj atsauce uz Winsock bibliotēkas failu Ws2_32. lib, lai kodu kompilētu.

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

atpakaļ uz sākumu

Norādiet servera lietojumprogrammas koda paraugu, myserver. CPP

Piezīme. Ir jāiekļauj atsauce uz Winsock bibliotēkas failu Ws2_32. lib, lai kodu kompilētu.

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

atpakaļ uz sākumu

Pārbaudīt paraugu

  1. Komandu uzvednē ierakstiet myserver. exe 4444 , lai startētu serveri portā 4444. servera lietojumprogrammā tiek parādīts šāds ziņojums, un pēc tam gaidiet klientiem:

    Serveris ir gatavs!

  2. Citā komandu uzvednes logā ierakstietMyClient ip adrešu 4444 , lai startētu klientu. Piezīme. Vietturis IP adrese ir servera IP adreses vietturis.

  3. Serverī tiek parādīts ziņojums, kas līdzīgs šim:

    Waiting.Waiting*..*Kad saņemat iepriekšējo ziņojumu, klientam ir jāieraksta aBcDeFgHi 10 sekunžu laikā pirms servera darbības turpināšanas.

Pēc aptuveni 10 sekundēm serveris rāda tālāk norādītās darbības.

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: skaitli FFFFFFFFFF [OOB]: HHHHHHHHHH [Normal]: aaaaaaaaaa [Normal]: cccccccccc [Normal]: eeeeeeeeee [Normal]: ggggggggggatpakaļ uz sākumu

Atsauces

Lai iegūtu papildinformāciju, apmeklējiet šīs Microsoft izstrādātāju tīkla (MSDN) tīmekļa vietnes:

No protokola neatkarīgas datuhttp://msdn2.Microsoft.com/en-us/library/ms740102.aspxWinsock funkcijashttp://msdn2.Microsoft.com/en-us/library/ms741394.aspxLai iegūtu papildinformāciju, noklikšķiniet uz šī raksta numura, lai skatītu Microsoft zināšanu bāzes rakstu:

331756 Funkcija Ioctlsocket nevar noteikt iekļautos ārpus diapazona esošo datuatpakaļ uz sākumu

Vai nepieciešama papildu palīdzība?

Paplašiniet savas prasmes
Iepazīties ar apmācību
Esiet pirmais, kas saņem jaunās iespējas
Pievienoties Microsoft Insider

Vai šī informācija bija noderīga?

Paldies par jūsu atsauksmēm!

Paldies par atsauksmēm! Šķiet, ka jums varētu būt noderīgi sazināties ar kādu no mūsu Office atbalsta speciālistiem.

×