Jak odeslat více bajt out-of-band data pomocí rozhraní Winsock


Souhrn


Někdy je nutné odeslat naléhavá data out-of-band (OOB) do vašeho přijímače. Přijímače jsou někteří uživatelé nebo aplikace, které zobrazí data. Chcete tato data OOB považovány za data vyšší prioritu než normální, může odeslat data. Pokud chcete odeslat data OOB je jeden bajt, můžete Vybrat funkce Hledat OOB data. Funkci recv číst data.

V protokolu TCP (Transmission Control), OOB blok dat je však vždy jeden bajt. Proto pokud odešlete data OOB více bajtů, se načte poslední bajt OOB data. Zbývající data se zachází stejně jako běžná data.

Pomocí ukázkový kód v tomto článku popisují, jak odeslat data OOB více bajtů pomocí Microsoft Windows Socket (Winsock).

ÚVOD


Tento článek popisuje, jak odeslat více bajtů (znaků) out-of-band data pomocí rozhraní Winsock. Ukázkové aplikace jsou vytvořeny v aplikaci Microsoft Visual C++.

Vzhledem k tomu, že tento mechanismus OOB není přímo podporována na úrovni soketů systému Windows, je nutné implementovat tento mechanismus OOB na úrovni aplikace. To není pravda OOB data. V této ukázkové aplikaci vytvoříte dva sokety na straně klienta, také známý jako straně odesílatele, odeslat OOB data a normální data. Na straně serveru, známé také jako straně příjemce pomocí dvou čipech zpracování dat ve dvou vláknech. Jedno vlákno je pro OOB data. Jiné vlákno je normální data.


Pro simulaci mechanismus OOB, je třeba provést synchronizaci těchto dvou podprocesů. Ujistěte se, že podproces, který zpracovává OOB data má vyšší prioritu než podproces, který zpracovává data normální.

Poznámka: Tato ukázková aplikace popisuje jeden způsob, jak odeslat data OOB vícebajtové. Bude pravděpodobně nutné zkontrolovat kód, chcete-li kód pracovat ve vašem prostředí.

zpět na horní

Vytvořit klientskou aplikaci

  1. Vytvořte klientskou aplikaci. V této aplikaci klienta následující ukázkový kód popisuje vytvoření dvou čipech:
    SOCKET myOOBSocket = INVALID_SOCKET;
    SOCKET myNormalSocket = INVALID_SOCKET;

    myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    Proměnná myOOBSocket se používá pro odesílání dat OOB. Proměnná myNormalSocket se používá pro odesílání dat normální.
  2. Protože data, která odešle myOOBSocket proměnná není pravda OOB data, musí mít způsob, jak zjistit, jaký druh dat, která měla soketu odeslat před zahájením odesílání dat serveru. Následující ukázkový kód popisuje způsob odesílání počáteční znak informovat server, o jaký druh dat, která bude odeslána. Použití "U" pro OOB data a použití "N" pro normální 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. Následující ukázkový kód popisuje, jak vytvořit klientskou aplikaci sledovat vstup od uživatele. Klientská aplikace odešle jako OOB data velká písmena. Duplicitní každý vstupní znak chcete-li vytvořit více bajtové řetězec.
    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);
    }
    ...
    }
Zobrazit ukázkový kód pro klientskou aplikaci, Myclient.cpp

zpět na horní

Vytvoření aplikace serveru

  1. Následující ukázkový kód na straně serveru popisuje jak vytvořit posluchače soketu ke sledování portu, který se používá ke komunikaci:
    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. Následující ukázkový kód popisuje, jak volat funkci přijmout Chcete-li čekat na příchozí připojení pokusí posluchače soketu:
    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. Pro simulaci mechanismus OOB, je třeba provést synchronizaci těchto dvou podprocesů. V této ukázkové aplikaci k tomu použijte globální objekt události . Vložte následující příkaz na začátku souboru.
    //Create a manual-reset event object. 
    hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Vytvoření obslužné rutiny OOB, která se spustí samostatný podproces. V této obslužné rutiny použijte funkci Vyberte zjistit při doručení dat. Při doručení dat, postupujte takto:
    1. Volání funkce ResetEvent nastavit do stavu bez signalizováno následným tichem objektu události hRecvEvent .
      ResetEvent(hRecvEvent);
      Toto nastavení zabraňuje běžných datových obslužnou rutinu, která je spuštěna v jiném vlákně.
    2. Volání funkce revc číst data.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Vzhledem k tomu, že lze zaslat více dat než vejde do vyrovnávací paměti, volání funkce recv opět s příznakem MSG_PEEK , chcete-li zjistit, zda data je k dispozici na lince.
      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
      Použijte některou z následujících metod, podle toho, zda je dat až ke čtení, nebo ne:
      • Pokud žádná data ke čtení čeká, voláním funkce SetEvent zadané události objektu nastavena na signalizovaném stavu.
        SetEvent(hRecvEvent);
        Když toto provedete, můžete pokračovat vlákno normální dat obslužné rutiny. Běžná data obslužné rutiny vyčká objektu události hRecvEvent obnoví normální dat obslužné rutiny vlákna.
      • Pokud data je stále čeká na ke čtení, volání funkce recv znovu zobrazíte zbývající data. Vyhledejte znovu až do data.
    Opakujte kroky 4a až 4c byla přečtena všechna data čekající na vyřízení.
  5. Vytvoření obslužné rutiny normální data. Tato obslužná rutina je podobný obslužné rutiny OOB data normální dat obslužné rutiny volání funkce WaitForSingleObject normální dat obslužné rutiny zjistí, že data byla dodána.
    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.
Zobrazit ukázkový kód pro serverové aplikace, Myserver.cpp

zpět na horní

Zobrazit ukázkový kód pro klientskou aplikaci, Myclient.cpp

Poznámka: Musí obsahovat odkaz na soubor knihovny Winsock Ws2_32.lib, chcete-li kód zkompilovat.
#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;
}
zpět na horní

Zobrazit ukázkový kód pro serverové aplikace, Myserver.cpp

Poznámka: Musí obsahovat odkaz na soubor knihovny Winsock Ws2_32.lib, chcete-li kód zkompilovat.
#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;
}
zpět na horní

Zkušební vzorek

  1. Na příkazovém řádku zadejte Myserver.exe 4444 spustit server na portu 4444.

    Serverová aplikace se zobrazí následující zpráva a pak čeká klienty:

    Na serveru je připraven!
  2. V jiném okně příkazového řádku zadejte
    Chcete-li spustit klienta myClient Adresa_ip 4444 .

    Poznámka: Zástupný symbol IPAddress je zástupný symbol pro adresu IP serveru.
  3. Na serveru zobrazí zprávu, která je podobná následující:

    Waiting.Waiting*..*


    Předchozí zprávu obdržíte, musíte zadat aBcDeFgHi v klientském počítači během 10 sekund před server pokračuje.
Po asi 10 sekundách serveru zobrazí následující informace:
[OOB]: BBBBBBBBBB
[OOB]: DDDDDDDDDD
[OOB]: FFFFFFFFFF
[OOB]: HHHHHHHHHH

[Normální]: aaaaaaaaaa
[Normální]: cccccccccc
[Normální]: eeeeeeeeee

[Normální]: gggggggggg
[Normální]: iiiiiiiiii
zpět na horní

Odkazy


Další informace naleznete na následujících webech Microsoft Developer Network (MSDN):Další informace získáte kliknutím na následující číslo v článku databáze Microsoft Knowledge Base:

331756 funkce Ioctlsocket nemůže rozpoznat vložený out-of-band data

zpět na horní