Como enviar dados de várias bytes fora da banda usando Winsock

Resumo

Por vezes, é necessário enviar dados urgentes fora de banda (OOB) para os seus recetores. Os recetores são quaisquer utilizadores ou aplicações que recebam os dados. Pretende que estes dados OOB são tratados como dados de maior prioridade do que quaisquer dados normais que possa enviar. Se os dados OOB que pretende enviar forem um byte, pode utilizar a função selecionada para procurar dados OOB. Pode utilizar a função recv para ler os dados. No entanto, no Protocolo de Controlo de Transmissão (TCP), o bloco de dados OOB é sempre um byte. Portanto, se enviar dados OOB de byte múltiplo, apenas o último byte dos dados OOB é recuperado. Os restantes dados são tratados como dados normais. Este artigo utiliza código de amostra para descrever como enviar dados OOB de 20 bytes múltiplos utilizando a tomada do Microsoft Windows (Winsock).

NESTA TAREFA

INTRODUÇÃO

Este artigo descreve como enviar vários bytes (chars) dados fora da banda usando Winsock. As aplicações da amostra são criadas no Microsoft Visual C++. Uma vez que este mecanismo OOB não é suportado diretamente ao nível da tomada do Microsoft Windows, tem de implementar este mecanismo OOB ao nível da aplicação. Isto não são dados verdadeiros da OOB. Nesta aplicação de amostra, cria-se duas tomadas do lado do cliente, também conhecidas como o lado remetente, para enviar dados OOB e dados normais. No lado do servidor, também conhecido como lado recetor, utiliza-se duas tomadas para processar os dados em duas linhas. Uma linha é para dados OOB. O outro fio é para dados normais. Para simular o mecanismo OOB, tem de sincronizar estes dois fios. Certifique-se de que o fio que processa os dados OOB tem maior prioridade do que o fio que processa os dados normais.Nota Esta aplicação de amostra descreve uma maneira de enviar dados OOB de vários bytes. Poderá ter de rever o código para que o código funcione no seu ambiente.de volta ao topo

Criar uma aplicação de cliente

  1. Criar uma aplicação de cliente. Nesta aplicação ao cliente, o seguinte código de amostra descreve como criar duas tomadas:

    SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    A variável myOOBSocket é usada para enviar dados OOB. A variável myNormalSocket é usada para enviar dados normais.

  2. Como os dados que a variável myOOBSocket envia não são dados OOB verdadeiros, deve ter uma maneira de dizer ao servidor que tipo de dados que a tomada deve enviar antes de começar a enviar dados. O seguinte código de amostra descreve como enviar um personagem inicial para notificar o servidor sobre que tipo de dados serão enviados. Utilize "U" para os dados OOB e utilize "N" para dados normais.

    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. O seguinte código de amostra descreve como fazer a aplicação do cliente monitorizar a entrada do utilizador. A aplicação do cliente envia caracteres maiúsculas como dados OOB. Duplique cada personagem de entrada para compor uma corda de byte múltiplo.

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

Mostrar o código de amostra para a aplicação do cliente, Myclient.cpp de volta ao topo

Criar uma aplicação de servidor

  1. No lado do servidor, o seguinte código de amostra descreve como criar uma tomada de ouvinte para monitorizar a porta que é utilizada para a comunicação:

    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. O seguinte código de amostra descreve como chamar a função de aceitação para fazer com que a tomada do ouvinte aguarde as tentativas de ligação de entrada:

    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. Para simular o mecanismo OOB, tem de sincronizar estes dois fios. Nesta aplicação de amostra, utilize um objeto global do Evento para o fazer. Coloque a seguinte declaração no topo do ficheiro.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Crie o manipulador OOB que é executado por um fio separado. Neste manipulador, utiliza a função selecionada para detetar quando os dados chegam. Quando os dados chegarem, siga estes passos:

    1. Ligue para a função ResetEvent para definir o objeto do evento hRecvEvent para o estado não sinalizado.

      ResetEvent(hRecvEvent);

      Isto bloqueia o manipulador de dados normal que é executado em outro fio.

    2. Ligue para a função revc para ler os dados.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Como podem ser enviados mais dados do que o tampão pode conter, volte a ligar para a função recv, juntamente com a bandeira MSG_PEEK, para determinar se algum dado está disponível no fio.

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

      Utilize qualquer um dos seguintes métodos, dependendo se há dados pendentes para serem lidos ou não:

      • Se não houver dados pendentes para serem lidos, ligue para a função SetEvent para definir o objeto de evento especificado para o estado sinalizado.

        SetEvent(hRecvEvent);

        Quando o fizer, o fio normal do manipulador de dados pode ser retomado. O fio normal do manipulador de dados aguarda o objeto do evento hRecvEvent antes que o fio normal do manipulador de dados retome.

      • Se os dados ainda estiverem pendentes para serem lidos, ligue novamente para a função recv para ler os restantes dados. Então, procure novamente por dados pendentes.

    Repita os passos 4a a 4c até que todos os dados pendentes foram lidos.

  5. Crie o manipulador de dados normal. Este manipulador é semelhante ao manipulador de dados OOB, exceto que o manipulador de dados normal chama a função WaitForSingleObject quando o manipulador normal de dados deteta que os dados chegaram.

    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.

Mostrar o código de amostra para a aplicação do servidor, Myserver.cpp de volta ao topo

Exiba o código de amostra para a aplicação do cliente, Myclient.cpp

Nota Deve incluir uma referência ao ficheiro da Biblioteca Winsock, Ws2_32.lib, para fazer a compilação de código.

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

de volta ao topo

Exiba o código de amostra para a aplicação do servidor, Myserver.cpp

Nota Deve incluir uma referência ao ficheiro da Biblioteca Winsock, Ws2_32.lib, para fazer a compilação de código.

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

de volta ao topo

Testar a amostra

  1. Numa mensagem de comando, escreva Myserver.exe 4444 para iniciar o servidor na porta 4444.A aplicação do servidor apresenta a seguinte mensagem e, em seguida, aguarda os clientes:

    O servidor está pronto!

  2. Numa janela de solicitação de comando diferente,digite o ipAddress 4444 para iniciar o cliente. Nota O espaço reservado IPAddress é um espaço reservado para o endereço IP do servidor.

  3. O servidor apresenta uma mensagem semelhante à seguinte:

    Waiting.Waiting*..*Quando receber a mensagem anterior, deve escrever aBcDeFgHi no cliente dentro de 10 segundos antes de o servidor continuar.

Após cerca de 10 segundos, o servidor apresenta o seguinte:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [Normal]: aaaaaaaaaaaaaaaaaaaa [Normal]: cccccccccccccc [Normal]: eeeeeeeeeeeeeeeeeee [Normal]: gggggggggggg [Normal]:de volta ao topo

Referências

Para obter informações adicionais, visite os seguintes web sites da Microsoft Developer Network (MSDN):

Dados fora de banda independentes do protocolohttp://msdn2.microsoft.com/en-us/library/ms740102.aspxfunções winsockhttp://msdn2.microsoft.com/en-us/library/ms741394.aspxPara obter informações adicionais, clique no seguinte número de artigo para ver o artigo na Base de Conhecimento da Microsoft:

331756 A função Ioctlsocket não pode detetar dados fora de bandade volta ao topo

Precisa de mais ajuda?

Aumente os seus conhecimentos
Explore as formações
Seja o primeiro a obter novas funcionalidades
Aderir ao Microsoft insiders

As informações foram úteis?

Obrigado pelos seus comentários!

Obrigado pelo seu feedback! Parece que poderá ser benéfico reencaminhá-lo para um dos nossos agentes de suporte do Office.

×