Så skickar du data utanför bandet med hjälp av Winsock


Sammanfattning


Ibland måste du skicka brådskande data från OOB till dina mottagare. Mottagare är alla användare eller program som tar emot dessa data. Du vill att den här OOB-data ska behandlas som högre prioritets data än vanliga data som du kan skicka. Om de OOB-data som du vill skicka är en byte, kan du använda funktionen Select för att leta efter OOB-data. Du kan använda funktionen Recv för att läsa data. I TCP (Transmission Control Protocol) är dock data blocket för OOB alltid ett byte. Om du skickar data för en OOB-byte, hämtas endast den sista byten för OOB-data. Återstående data behandlas som vanliga data. I den här artikeln används exempel kod för att få information om hur du skickar data i Real storlek med hjälp av Microsoft Windows socket (Winsock).

Introduktion


I den här artikeln beskrivs hur du skickar data utanför band (tecken) med hjälp av Winsock. Exempel programmen skapas i Microsoft Visual C++. Eftersom denna OOB-mekanism inte är direkt kompatibel på Microsoft Windows socket-nivå måste du implementera denna OOB-mekanism på program nivå. Det här är inte äkta OOB-data. I det här exemplet skapar du två socketar på klient sidan, som även kallas avsändarens sida, för att skicka OOB-data och normala data. På Server sidan, som även kallas mottagaren, använder två Sockets för att bearbeta data i två trådar. En tråd är för OOB-data. Den andra tråden är för normal data. Om du vill simulera OOB-mekanismen måste du synkronisera dessa två trådar. Kontrol lera att tråden som bearbetar OOB-data har högre prioritet än den tråd som bearbetar normala data.Obs! I det här exempel programmet beskrivs ett sätt att skicka OOB-data på flera byte. Du kanske måste ändra koden för att koden ska fungera i din miljö.tillbaka till början

Skapa ett klient program

  1. Skapa ett klient program. I det här klient programmet beskrivs följande exempel kod för hur du skapar två socketar:
    SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    Variabeln myOOBSocket används för att skicka OOB-data. Variabeln myNormalSocket används för att skicka normal data.
  2. Eftersom de data som myOOBSocket -variabeln skickar inte är äkta OOB-data måste du ha ett sätt att ange vilken typ av data som socketen ska skicka innan du börjar skicka data. Följande exempel kod beskriver hur du skickar ett första tecken för att meddela servern om vilken typ av data som ska skickas. Använd "U" för OOB-data och Använd "N" för normala data.
    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. Följande exempel kod innehåller information om hur du gör att klient programmet övervakar inmatningen från användaren. Klient programmet skickar versaler som OOB-data. Duplicera varje tecken för inmatning för att skapa en sträng med flera byte.
    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);}...}
Visa exempel koden för klient programmet, min klient. cpptillbaka till början

Skapa en server tillämpning

  1. På Server sidan nedan beskrivs hur du skapar en lyssnarsocket för att övervaka porten som används för kommunikation:
    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. Följande exempel kod beskriver hur du anropar funktionen acceptera för att få lyssnar-socket vänta på inkommande anslutnings försök:
    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. Om du vill simulera OOB-mekanismen måste du synkronisera dessa två trådar. I det här exemplet använder du ett globalt händelse objekt för att göra detta. Ange följande uttryck högst upp i filen.
    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Skapa den OOB-hanterare som körs av en separat tråd. I den här hanteraren använder du funktionen Markera för att upptäcka när data kommer in. När data anländer följer du de här stegen:
    1. Anropa funktionen ResetEvent om du vill ange händelse objekt för hRecvEvent till icke-avsignalad.
      ResetEvent(hRecvEvent);
      Då blockeras den vanliga data hanteraren som körs i en annan tråd.
    2. Med funktionen revc kan du läsa data.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Eftersom fler data kan skickas än vad bufferten rymmer kan du anropa funktionen Recv igen tillsammans med flaggan MSG_PEEK för att avgöra om det finns några data tillgängliga på sladden.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
      Använd någon av följande metoder, beroende på om det finns data som väntar på att läsas eller inte:
      • Om det inte finns några data att läsa kan du anropa funktionen SetEvent för att ange det angivna händelseobjektet till läget signaled.
        SetEvent(hRecvEvent);
        När du gör det kan du aktivera den vanliga data hanterarens tråd. Den vanliga data hanterarens tråd väntar på hRecvEvent innan den vanliga data hanterarens tråd återupptas.
      • Om det fortfarande går att läsa data kan du anropa funktionen Recv igen för att läsa återstående data. Titta igen efter väntande data.
    Upprepa steg 4a till och med 1 till och med 1 och sedan alla pågående uppgifter har lästs.
  5. Skapa data hanteraren normal. Den här hanteraren liknar OOB-datahanteringen förutom att den vanliga data hanteraren anropar funktionen WaitForSingleObject när data hanteras.
    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.
Visa exempel koden för serverprogrammet, min server. cpptillbaka till början

Visa exempel koden för klient programmet, client. cpp

Obs! Du måste ange en referens till Winsock-Library-filen, Ws2_32. lib, för att kompilera koden.
#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;}
tillbaka till början

Visa exempel koden för serverprogrammet, mappen Server. cpp

Obs! Du måste ange en referens till Winsock-Library-filen, Ws2_32. lib, för att kompilera koden.
#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;}
tillbaka till början

Testa provet

  1. Skriv ' Server. exe 4444 i kommando tolken för att starta servern på port 4444. serverprogrammet visar följande meddelande och väntar sedan på klienter:
    Servern är klar!
  2. I ett annat kommando fönster anger du clientIPAddress 4444 för att starta klienten. Obs! Plats hållaren IPAddress är en plats hållare för SERVERns IP-adress.
  3. Servern visar ett meddelande som ser ut ungefär så här:
    Waiting.Waiting*..*
    När du får det föregående meddelandet måste du skriva aBcDeFgHi på klienten inom 10 sekunder innan servern fortsätter.
Efter cirka 10 sekunder visar servern följande:
[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: aaaaaaaaaa [normal]: cccccccccc [normal]: eeeeeeeeee [normal]: gggggggggg [normal]: iiiiiiiiii
överst på sidan

Referenser


Mer information finns på följande MSDN-webbplatser (Microsoft Developer Network):Om du vill veta mer klickar du på följande artikel nummer och läser artikeln i Microsoft Knowledge Base:
331756 Ioctlsocket-funktionen kan inte upptäcka infogade data utanför band
överst på sidan