Het verzenden van gegevens van meerdere bytes out-of-band met behulp van Winsock


Samenvatting


Soms moet u dringend out of band (OOB) data naar uw ontvangers sturen. Ontvangers zijn gebruikers of toepassingen die de gegevens ontvangen. U wilt de gegevens van deze OOB behandeld als gegevens van hogere prioriteit dan de normale gegevens die u kunt verzenden. Als één byte is de OOB-gegevens die u wilt verzenden, kunt u de functie selecteren om te zoeken naar gegevens OOB. U kunt de functie ontvangen om de gegevens te lezen.

Transmission Control Protocol (TCP) is het gegevensblok OOB echter altijd één byte. Als u meerdere bytes OOB-gegevens verzendt, wordt daarom alleen de laatste byte van het OOB-gegevens opgehaald. De resterende gegevens wordt behandeld als normale gegevens.

In dit artikel wordt voorbeeldcode gebruikt om te beschrijven hoe u meerdere bytes OOB om gegevens te verzenden via Microsoft Windows Sockets (Winsock).

INLEIDING


Dit artikel wordt beschreven hoe u meerdere-bytes (tekens) out-of-band om gegevens te verzenden met behulp van Winsock. De voorbeeldtoepassingen worden gemaakt in Microsoft Visual C++.

Omdat dit mechanisme OOB niet rechtstreeks op het niveau van de socket Microsoft Windows wordt ondersteund, moet u dit mechanisme OOB op toepassingsniveau implementeren. Dit is niet waar OOB-gegevens. In deze voorbeeldtoepassing maakt u twee sockets op de client, ook bekend als kant van de afzender, OOB en normale gegevens verzenden. Op de server, ook wel bekend als de ontvanger aan de zijkant, met twee sockets kunt u de gegevens in de twee threads. Er is één thread voor OOB-gegevens. De andere thread is voor normale gegevens.


Als u wilt simuleren het OOB-mechanisme, moet u deze twee threads te synchroniseren. Zorg dat de thread die OOB gegevens verwerkt heeft hogere prioriteit dan de thread die de normale gegevens verwerkt.

Opmerking Deze voorbeeldtoepassing wordt beschreven een manier om multibyte OOB-gegevens te verzenden. U moet de code om de code in uw omgeving werkt herzien.

terug naar boven

Een clienttoepassing maken

  1. Maak een clienttoepassing. In deze clienttoepassing, is de volgende voorbeeldcode wordt beschreven hoe u twee sockets maken:
    SOCKET myOOBSocket = INVALID_SOCKET;
    SOCKET myNormalSocket = INVALID_SOCKET;

    myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    De variabele myOOBSocket wordt gebruikt om OOB-gegevens te verzenden. De variabele myNormalSocket wordt gebruikt voor het verzenden van normale gegevens.
  2. Omdat de gegevens die worden verzonden met de variabele myOOBSocket niet gelijk is aan true OOB-gegevens, moet u een manier om de server laten weten wat voor soort gegevens dat de socket te verzenden voordat u begint met het verzenden van gegevens moet hebben. De volgende voorbeeldcode wordt beschreven hoe u voor het verzenden van een eerste teken op de hoogte stellen van de server over wat voor gegevens die worden verzonden. OOB gegevens gebruik "U" en "N" te gebruiken voor normale gegevens.
    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. De volgende voorbeeldcode wordt beschreven hoe u de clienttoepassing controleren van de invoer van de gebruiker. De clienttoepassing verzendt tekens in hoofdletters als OOB-gegevens. Dubbele elke ingevoerde teken om het opstellen van een tekenreeks van meerdere bytes.
    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);
    }
    ...
    }
De voorbeeldcode voor de clienttoepassing, Myclient.cpp weergeven

terug naar boven

Een server-toepassing maken

  1. Op de server, wordt de volgende voorbeeldcode beschreven hoe voor het maken van een listener-socket voor het controleren van de poort die wordt gebruikt voor communicatie:
    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. De volgende voorbeeldcode wordt beschreven hoe u de functie accepteren om de listener-socket wachten op binnenkomende verbinding probeert te maken:
    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. Als u wilt simuleren het OOB-mechanisme, moet u deze twee threads te synchroniseren. Gebruik een globale Event -object om dit te doen in deze voorbeeldtoepassing. De volgende instructie aan het begin van het bestand geplaatst.
    //Create a manual-reset event object. 
    hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. De OOB-handler die wordt uitgevoerd door een aparte thread maken. In deze handler gebruikt u de functie selecteren om op te sporen wanneer gegevens aankomen. Wanneer gegevens aankomen, als volgt te werk:
    1. Roep de functie ResetEvent om de hRecvEvent event-object ingesteld op de status niet gesignaleerd.
      ResetEvent(hRecvEvent);
      De normale gegevens-handler die wordt uitgevoerd in een andere thread wordt geblokkeerd.
    2. De revc functie aanroepen om te lezen van de gegevens.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Omdat er meer gegevens kunnen worden verzonden dan de buffer kan bevatten, roept u de functie ontvangen , samen met de vlag MSG_PEEK om te bepalen of alle gegevens op het netwerk beschikbaar is.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
      Gebruik een van de volgende methoden, afhankelijk van of er gegevens in behandeling is of niet worden gelezen:
      • Als er geen gegevens in behandeling moeten worden gelezen, roept u de functie SetEvent om de opgegeven gebeurtenisobject instellen op de gesignaleerde status.
        SetEvent(hRecvEvent);
        Wanneer u dit doet, wordt de normale gegevens-handler thread kunt hervatten. Het gebeurtenisobject hRecvEvent wacht de normale gegevens-handler thread voordat de normale gegevens-handler thread wordt hervat.
      • Gegevens is nog in behandeling te lezen is, belt de functie ontvangen opnieuw te lezen van de resterende gegevens. Vervolgens zoekt u opnieuw in de behandeling van gegevens.
    Herhaal stap 4a tot en met 4c totdat alle wachtende gegevens zijn gelezen.
  5. De normale gegevens-handler maken. Deze handler is vergelijkbaar met de handler OOB-gegevens, met dien verstande dat de normale gegevens-handler de functie WaitForSingleObject roept wanneer de normale gegevens-handler wordt opgemerkt dat de gegevens heeft ontvangen.
    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.
De voorbeeldcode voor de brontoepassing, Myserver.cpp weergeven

terug naar boven

De voorbeeldcode voor de clienttoepassing, Myclient.cpp weergeven

Opmerking U moet een verwijzing naar de Winsock-bestand, Ws2_32.lib, zodat de code compileren opnemen.
#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;
}
terug naar boven

De voorbeeldcode voor de brontoepassing, Myserver.cpp weergeven

Opmerking U moet een verwijzing naar de Winsock-bestand, Ws2_32.lib, zodat de code compileren opnemen.
#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;
else
printf("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;
}
terug naar boven

Het voorbeeld testen

  1. Typ bij een opdrachtprompt 4444 Myserver.exe als u de server op poort 4444.

    De servertoepassing wordt het volgende bericht wordt weergegeven en vervolgens wacht tot clients:

    De server is gereed!
  2. Typ in een ander opdrachtpromptvenster
    myClient IP-adres 4444 naar de client wordt gestart.

    Opmerking Tijdelijke aanduiding voor de IP-adres is een tijdelijke aanduiding voor het IP-adres van de server.
  3. De server wordt een bericht weergegeven dat lijkt op het volgende:

    Waiting.Waiting*..*


    Wanneer u het vorige bericht ontvangt, moet u aBcDeFgHi typen op de client binnen 10 seconden voordat de server wordt voortgezet.
Na ongeveer 10 seconden, de server wordt het volgende weergegeven:
[OOB]: BBBBBBBBBB
[OOB]: DDDDDDDDDD
[OOB]: FFFFFFFFFF
[OOB]: HHHHHHHHHH

[Normal]: aaaaaaaaaa
[Normal]: cccccccccc
[Normal]: eeeeeeeeee

[Normal]: gggggggggg
[Normal]: iiiiiiiiii
terug naar boven

Referenties


Ga naar de volgende websites van Microsoft Developer Network (MSDN) voor meer informatie:Voor meer informatie klikt u op het volgende artikel in de Microsoft Knowledge Base:

331756 de Ioctlsocket functie kan niet in line out-of-band-gegevens detecteren

terug naar boven