Resumen

En ocasiones, debe enviar datos urgentes fuera de banda (OOB) a sus receptores. Los receptores son cualquier usuario o cualquier aplicación que reciba los datos. Desea que estos datos OOB se traten como datos de mayor prioridad que cualquier tipo de datos normales que pueda enviar. Si los datos OOB que desea enviar son de un byte, puede usar la función seleccionar para buscar datos OOB. Puede usar la función RECV para leer los datos. Sin embargo, en el protocolo de control de transmisión (TCP), el bloque de datos OOB siempre tiene un byte. Por lo tanto, si envía datos OOB de varios bytes, solo se recupera el último byte de los datos OOB. Los datos restantes se tratan como datos normales. En este artículo se usa código de ejemplo para describir cómo enviar datos OOB de varios bytes mediante Microsoft Windows socket (Winsock).

EN ESTA TAREA

INTRODUCCIÓN

En este artículo se describe cómo enviar datos fuera de banda de varios bytes (caracteres) mediante Winsock. Las aplicaciones de ejemplo se crean en Microsoft Visual C++. Dado que este mecanismo OOB no se admite directamente en el nivel de socket de Microsoft Windows, debe implementar este mecanismo OOB en el nivel de la aplicación. Esto no es un verdadero dato OOB. En esta aplicación de ejemplo, puede crear dos sockets en el cliente, también conocido como el lado del remitente, para enviar datos OOB y datos normales. En el servidor, también conocido como receptor, se usan dos sockets para procesar los datos en dos subprocesos. Un subproceso es para los datos OOB. El otro subproceso es para datos normales. Para simular el mecanismo OOB, debe sincronizar estos dos subprocesos. Asegúrese de que el subproceso que procesa los datos OOB tiene mayor prioridad que el subproceso que procesa los datos normales.Nota Esta aplicación de ejemplo describe una manera de enviar datos OOB de varios bytes. Es posible que tenga que revisar el código para que el código funcione en su entorno.volver al principio

Crear una aplicación cliente

  1. Crear una aplicación cliente. En esta aplicación cliente, en el siguiente código de ejemplo se describe cómo crear dos sockets:

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

    La variable myOOBSocket se usa para enviar datos OOB. La variable myNormalSocket se usa para enviar datos normales.

  2. Debido a que los datos que la variable myOOBSocket envía no son verdaderos datos OOB, debe tener una forma de indicar al servidor qué tipo de datos se supone que debe enviar el socket antes de empezar a enviar datos. En el siguiente código de ejemplo se describe cómo enviar un carácter inicial para notificar al servidor el tipo de datos que se enviará. Use "U" para los datos OOB y use "N" para los datos normales.

    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. En el siguiente código de ejemplo se describe cómo hacer que la aplicación cliente supervise la entrada del usuario. La aplicación cliente envía caracteres en mayúsculas como datos OOB. Duplique cada carácter de entrada para redactar una cadena de varios 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);}...}

Mostrar el código de ejemplo de la aplicación cliente, mi Client. cpp al principio

Crear una aplicación de servidor

  1. En el servidor, el siguiente código de ejemplo describe cómo crear un socket de escucha para supervisar el puerto que se usa para la comunicación:

    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. El siguiente código de ejemplo describe cómo llamar a la función Accept para que el socket de escucha espere los intentos de conexión entrante:

    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 el mecanismo OOB, debe sincronizar estos dos subprocesos. En esta aplicación de ejemplo, use un objeto de evento global para hacerlo. Coloque la siguiente instrucción en la parte superior del archivo.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Crear el controlador OOB ejecutado por un subproceso independiente. En este controlador, use la función seleccionar para detectar cuando llegan los datos. Cuando lleguen los datos, siga estos pasos:

    1. Llama a la función ResetEvent para establecer el objeto de evento hRecvEvent en el estado no señalado.

      ResetEvent(hRecvEvent);

      Esto bloquea el controlador de datos normal que se ejecuta en otro subproceso.

    2. Llama a la función RevC para leer los datos.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Dado que es posible que se envíen más datos de los que puede almacenar el búfer, vuelve a llamar a la función RECV , junto con el marcador MSG_PEEK , para determinar si hay datos disponibles en el cable.

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

      Use cualquiera de los métodos siguientes, en función de si hay datos pendientes de lectura o no:

      • Si no hay datos pendientes de lectura, llame a la función SetEvent para establecer el objeto de evento especificado en el estado señalado.

        SetEvent(hRecvEvent);

        Al hacerlo, se puede reanudar el subproceso de controlador de datos normal. El subproceso de controlador de datos normal espera el objeto de evento hRecvEvent antes de que se reanude el subproceso de controlador de datos normal.

      • Si los datos aún están pendientes de lectura, vuelva a llamar a la función RECV para leer los datos restantes. A continuación, busque de nuevo los datos pendientes.

    Repita los pasos 4A a 4C hasta que se hayan leído todos los datos pendientes.

  5. Cree el controlador de datos normal. Este controlador es similar al controlador de datos OOB, excepto en que el controlador de datos normal llama a la función WaitForSingleObject cuando el controlador de datos normal detecta que han llegado los datos.

    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 el código de ejemplo de la aplicación de servidor, servidor. cpp, volver al principio

Mostrar el código de ejemplo de la aplicación cliente, mi Client. cpp

Nota Debe incluir una referencia al archivo de biblioteca Winsock, Ws2_32. lib, para hacer que se compile el 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;}

volver al principio

Mostrar el código de ejemplo de la aplicación de servidor, servidor. cpp

Nota Debe incluir una referencia al archivo de biblioteca Winsock, Ws2_32. lib, para hacer que se compile el 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;}

volver al principio

Probar la muestra

  1. En un símbolo del sistema, escriba mi . exe 4444 para iniciar el servidor en el puerto 4444. la aplicación de servidor mostrará el siguiente mensaje y esperará a los clientes:

    El servidor está listo.

  2. En otra ventana del símbolo del sistema, escriba micliente IPAddress 4444 para iniciar el cliente. Nota La dirección IP del marcador de posición es un marcador de posición para la dirección IP del servidor.

  3. El servidor muestra un mensaje similar al siguiente:

    Waiting.Waiting*..*Cuando reciba el mensaje anterior, debe escribir aBcDeFgHi en el cliente en un lapso de 10 segundos antes de que el servidor continúe.

Después de unos 10 segundos, el servidor muestra lo siguiente:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: Aaaaaaaaaa [normal]: cccccccccc [normal]: eeeeeeeeee [normal]: gggggggggg [normal]: iiiiiiiiiivolver al principio

Referencias

Para obtener más información, visite los siguientes sitios web de Microsoft Developer Network (MSDN):

Características de datos fuera de bandahttp://msdn2.Microsoft.com/en-us/library/ms740102.aspxWinsockhttp://msdn2.Microsoft.com/en-us/library/ms741394.aspxPara obtener información adicional, haga clic en el número de artículo siguiente para verlo en Microsoft Knowledge Base:

331756 La función Ioctlsocket no puede detectar datos fuera de banda en líneavolver al principio

¿Necesita más ayuda?

Ampliar sus conocimientos
Explorar los cursos
Obtener nuevas características primero
Unirse a Microsoft Insider

¿Le ha sido útil esta información?

¿Cómo de satisfecho está con la calidad de la traducción?
¿Qué ha afectado a tu experiencia?

¡Gracias por sus comentarios!

×