Mit Winsock mehrere Byte Out-of-Band-Daten senden


Zusammenfassung


Manchmal müssen Sie dringende Out-of-Band (OOB) Daten an die Empfänger senden. Empfänger sind Benutzer oder Programme, die die Daten empfängt. Soll diese OOB-Daten als eine normalen Daten möglicherweise als höhere Priorität behandelt. Ist zu sendenden OOB-Daten byteweise können Function auswählen Sie OOB-Daten suchen. Die Empfangsfunktion können Sie die Daten lesen.

Allerdings steht in Transmission Control Protocol (TCP) OOB-Datenblock ein Byte. Wenn mehrere Byte OOB-Daten senden, wird das letzte Byte von OOB-Daten abgerufen. Die restlichen Daten werden wie normale Daten.

In diesem Artikel verwendet Beispielcode an mehreren Byte OOB-Daten mithilfe von Microsoft Windows Socket (Winsock) beschreiben.

Einführung


Dieser Artikel beschreibt, wie mehrere Bytes (Zeichen) Out-of-Band-Daten mithilfe von Winsock senden. Beispiel-Applikationen werden in Microsoft Visual C++ erstellt.

Da diese OOB nicht direkt auf Microsoft Windows unterstützt wird, müssen Sie diesen OOB-Mechanismus auf Anwendungsebene implementieren. Dies ist keine echte OOB-Daten. In dieser Anwendung erstellen Sie zwei Sockets auf der Clientseite auch der Senderseite OOB-Daten und Daten. Auf der Serverseite auch die Empfängerseite verwenden Sie zwei Sockeln zum Verarbeiten der Daten in zwei Threads. Ein Thread ist für OOB-Daten. Der Thread ist für normale Daten.


Um den OOB-Mechanismus zu simulieren, müssen Sie diese beiden Threads synchronisieren. Stellen Sie sicher, dass der Thread, der OOB Daten Vorrang vor den Threads, die Daten verarbeitet.

Hinweis Diese Anwendung beschreibt eine Möglichkeit zur Multi-Byte OOB-Daten senden. Möglicherweise bearbeiten den Code, um den Code in Ihrer Umgebung funktioniert.

zurück zum Anfang

Erstellen einer Clientanwendung

  1. Erstellen Sie eine Clientanwendung. In dieser Clientanwendung folgenden Code zwei Sockets erstellen beschrieben:
    SOCKET myOOBSocket = INVALID_SOCKET;
    SOCKET myNormalSocket = INVALID_SOCKET;

    myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    Die Variable MyOOBSocket wird zum Senden von OOB-Daten verwendet. Die Variable MyNormalSocket zum Senden von Daten.
  2. Da die Variable MyOOBSocket sendet Daten nicht true OOB-Daten sind, muss dem Server mitteilen, welche Daten sollen, das der Socket senden Sie vor dem Senden von Daten. Der folgende Code beschreibt das Senden ein Anfangszeichen Server über Daten benachrichtigt, die gesendet werden. "U" OOB-Daten und "N" für normale Daten.
    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. Der folgende Beispielcode beschreibt die Clientanwendung die Eingaben des Benutzers überwachen. Die Clientanwendung sendet Großbuchstaben als OOB-Daten. Doppelte jedes eingegebene Zeichen um eine Multiple-Byte-Zeichenfolge erstellen.
    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);
    }
    ...
    }
Den Beispielcode für die Clientanwendung Myclient.cpp anzeigen

zurück zum Anfang

Erstellen Sie eine Serveranwendung

  1. Auf der Serverseite beschrieben der folgende Beispielcode erstellen einen Listenersocket den Port überwachen, der für die Kommunikation verwendet wird:
    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. Der folgende Code beschrieben rufen Sie die Funktion Übernehmen zu warten eingehenden Verbindungsversuchen Listenersocket:
    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. Um den OOB-Mechanismus zu simulieren, müssen Sie diese beiden Threads synchronisieren. In diesem beispielanwendung dazu verwenden Sie ein globales Ereignis -Objekt. Platzieren Sie die folgende Anweisung am Anfang der Datei.
    //Create a manual-reset event object. 
    hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Erstellen Sie den OOB-Ereignishandler, der von einem separaten Thread ausgeführt wird. In diesem Handler verwenden Sie die Funktion Wählen Sie Daten erkennen. Wenn Daten ankommen, gehen Sie folgendermaßen vor:
    1. Aufruf der Funktion ResetEvent , um das Ereignisobjekt hRecvEvent in den nicht signalisierten Zustand festzulegen.
      ResetEvent(hRecvEvent);
      Dadurch wird verhindert, normale Daten-Handler, der in einem anderen Thread ausgeführt wird.
    2. Rufen Sie die Funktion Revc , um die Daten lesen.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Da mehr Daten gesendet werden können, als der Puffer aufnehmen kann, erneut die Empfangsfunktion , und das Flag MSG_PEEK bestimmen, ob Daten bei der Übertragung verfügbar ist.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
      Verwenden Sie eine der folgenden Methoden an, je nachdem, ob Daten werden oder nicht gelesen werden:
      • Wenn keine Daten gelesen werden ausstehende, rufen Sie die Funktion SetEvent zum angegebenen Ereignisobjekt in den signalisierten Zustand festgelegt.
        SetEvent(hRecvEvent);
        Wenn Sie dies tun, kann Daten Handler Thread fort. Der normalen Ereignishandler Thread wartet hRecvEvent Ereignisobjekt bevor Daten Handler Thread fortgesetzt wird.
      • Rufen Daten ist noch gelesen, die Empfangsfunktion erneut, um die restlichen Daten lesen . Suchen Sie dann erneut anstehenden Daten.
    Wiederholen Sie die Schritte 4a bis 4c, bis alle ausstehende Daten gelesen wurden.
  5. Normale Daten-Handler zu erstellen. Dieser Handler ähnelt OOB-Daten-Handler mit normalen Daten-Handler WaitForSingleObject -Funktion aufruft, wenn der Handler Daten erkennt, dass Daten eingetroffen.
    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.
Beispielcode für die Server-Anwendung Myserver.cpp anzeigen

zurück zum Anfang

Den Beispielcode für die Clientanwendung Myclient.cpp anzeigen

Hinweis Sie müssen einen Verweis auf die Winsock-Bibliothek-Datei Ws2_32.lib, um den Code zu kompilieren enthalten.
#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;
}
zurück zum Anfang

Beispielcode für die Server-Anwendung Myserver.cpp anzeigen

Hinweis Sie müssen einen Verweis auf die Winsock-Bibliothek-Datei Ws2_32.lib, um den Code zu kompilieren enthalten.
#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;
}
zurück zum Anfang

Testen des Beispiels

  1. Geben Sie eine Befehlszeile Myserver.exe 4444 zum Starten des Servers auf Port 4444.

    Die Server-Anwendung zeigt folgende Meldung an und wartet dann auf Clients:

    Der Server ist bereit.
  2. Geben Sie in einem anderen Fenster
    So starten Sie den Client MyClient IPAddress 4444 .

    Hinweis Platzhalter IP-Adresse steht für die IP-Adresse des Servers.
  3. Der Server wird eine Meldung ähnlich der folgenden angezeigt:

    Waiting.Waiting*..*


    Wenn Sie die Nachricht erhalten, geben Sie aBcDeFgHi auf dem Client innerhalb von 10 Sekunden, bevor der Server.
Nach ca. 10 Sekunden wird der Server Folgendes angezeigt:
[OOB]: BBBBBBBBBB
[OOB]: DDDDDDDDDD
[OOB]: FFFFFFFFFF
[OOB]: HHHHHHHHHH

[Normal]: Aaaaaaaaaa
[Normal]: Cccccccccc
[Normal]: Eeeeeeeeee

[Normal]: Gggggggggg
[Normal]: Iiiiiiiiii
zurück zum Anfang

Referenzen


Weitere Informationen finden Sie auf folgenden Websites von Microsoft Developer Network (MSDN):Klicken Sie für Weitere Informationen auf die folgende Artikelnummer, um den Artikel der Microsoft Knowledge Base anzuzeigen:

331756 der Ioctlsocket-Funktion nicht Inline Out-of-Band-Daten erkannt.

zurück zum Anfang