使用 Microsoft 登录
登录或创建帐户。
你好,
使用其他帐户。
你有多个帐户
选择要登录的帐户。

摘要

有时,您必须向接收器发送紧急带外(OOB)数据。 接收器是任何用户或接收数据的任何应用程序。 你希望此 OOB 数据被视为比你可能发送的任何普通数据更高优先级的数据。 如果要发送的 OOB 数据是一个字节,则可以使用 select 函数查找 OOB 数据。 可以使用 " 接收 " 功能读取数据。但是,在传输控制协议(TCP)中,OOB 数据块始终为一个字节。 因此,如果发送多字节 OOB 数据,则仅检索 OOB 数据的最后一个字节。 剩余数据的处理方式类似于普通数据。本文使用示例代码介绍如何使用 Microsoft Windows 套接字(Winsock)发送多字节 OOB 数据。

本任务的内容

简介

本文介绍如何使用 Winsock 发送多字节(chars)带外数据。 示例应用程序是在 Microsoft Visual c + + 中创建的。由于此 OOB 机制在 Microsoft Windows 套接字级别上不直接受支持,因此必须在应用程序级别实现此 OOB 机制。 这不是真正的 OOB 数据。 在此示例应用程序中,你将在客户端创建两个套接字(也称为 "发件人端")以发送 OOB 数据和普通数据。 在服务器端(也称为接收方)上,使用两个套接字处理两个线程中的数据。 一个线程用于 OOB 数据。 另一个线程用于正常数据。 若要模拟 OOB 机制,必须同步这两个线程。 请确保处理 OOB 数据的线程比处理普通数据的线程具有更高的优先级。注意 此示例应用程序介绍一种发送多字节 OOB 数据的方法。 你可能需要修订代码才能使代码在你的环境中正常工作。返回页首

创建客户端应用程序

  1. 创建客户端应用程序。 在此客户端应用程序中,以下示例代码介绍了如何创建两个套接字:

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

    MyOOBSocket变量用于发送 OOB 数据。 MyNormalSocket变量用于发送普通数据。

  2. 由于 myOOBSocket 变量发送的数据不是真正的 OOB 数据,因此你必须有一种方法来告诉服务器套接字在开始发送数据之前应该发送哪种数据。 以下示例代码介绍了如何发送初始字符以通知服务器将发送哪种类型的数据。 对 OOB 数据使用 "U",并对普通数据使用 "N"。

    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. 以下示例代码介绍了如何使客户端应用程序监视来自用户的输入。 客户端应用程序以 OOB 数据的形式发送大写字母字符。 复制每个输入字符以撰写多字节字符串。

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

显示客户端应用程序的示例代码,Myclient 返回页首

创建服务器应用程序

  1. 在服务器端,以下示例代码介绍了如何创建侦听器套接字来监视用于通信的端口:

    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. 以下示例代码介绍了如何调用 accept 函数以使监听器套接字等待传入的连接尝试:

    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. 若要模拟 OOB 机制,必须同步这两个线程。 在此示例应用程序中,使用全局 事件 对象执行此操作。 将以下语句放在文件的顶部。

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. 创建由单独的线程执行的 OOB 处理程序。 在此处理程序中,使用 select 函数检测数据何时到达。 当数据到达时,请按照下列步骤操作:

    1. 调用 ResetEvent 函数以将 hRecvEvent 事件对象设置为非终止状态。

      ResetEvent(hRecvEvent);

      这将阻止在另一个线程中执行的正常数据处理程序。

    2. 调用 revc 函数以读取数据。

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. 由于可能会发送比缓冲区可以保留的数据更多的数据,因此使用MSG_PEEK标志再次调用接收函数,以确定线路上是否有任何数据可用。

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

      使用以下任一方法,具体取决于是否存在待读取的数据:

      • 如果没有要读取的数据,请调用 SetEvent 函数以将指定的事件对象设置为终止状态。

        SetEvent(hRecvEvent);

        执行此操作时,可以恢复正常的数据处理程序线程。 正常数据处理程序线程在正常数据处理程序线程继续之前等待 hRecvEvent 事件对象。

      • 如果仍有等待读取的数据,请再次调用 接收 函数以读取剩余的数据。 然后,再次查看待处理数据。

    重复步骤4a 到4c,直到已读取所有待处理的数据。

  5. 创建普通数据处理程序。 此处理程序类似于 OOB 数据处理程序,但当常规数据处理程序检测到数据已收到时,一般数据处理程序调用 WaitForSingleObject 函数。

    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.

服务器应用程序、Myserver .cpp 的示例代码显示回顶部

显示客户端应用程序的示例代码 Myclient

注意 必须包括对 Winsock 库文件的引用(Ws2_32 .lib)才能使代码编译。

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

返回页首

显示服务器应用程序、Myserver 的示例代码

注意 必须包括对 Winsock 库文件的引用(Ws2_32 .lib)才能使代码编译。

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

返回页首

测试示例

  1. 在命令提示符处,键入 Myserver 4444 以在端口4444上启动服务器。服务器应用程序显示以下消息,然后等待客户端:

    服务器已准备就绪!

  2. 在另一个命令提示符窗口中,键入MyClient IPAddress 4444 以启动客户端。 注意 占位符 " ip 地址" 是服务器 IP 地址的占位符。

  3. 服务器显示一条类似于以下内容的消息:

    Waiting.Waiting*..*当您收到以前的消息时,您必须在10秒内的客户端上键入 aBcDeFgHi ,然后服务器才能继续。

大约10秒钟后,服务器将显示以下内容:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: aaaaaaaaaa [normal]: cccccccccc [normal]: eeeeeeeeee [normal]: gggggggggg [normal]: iiiiiiiiii返回页首

参考

有关其他信息,请访问以下 Microsoft 开发人员网络(MSDN)网站:

独立于协议的带外数据Http://msdn2.microsoft.com/en-us/library/ms740102.aspxWinsock 函数http://msdn2.microsoft.com/en-us/library/ms741394.aspx有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:

331756 Ioctlsocket 函数无法检测内联带外数据返回页首

需要更多帮助?

需要更多选项?

了解订阅权益、浏览培训课程、了解如何保护设备等。

社区可帮助你提出和回答问题、提供反馈,并听取经验丰富专家的意见。

此信息是否有帮助?

你对语言质量的满意程度如何?
哪些因素影响了你的体验?
按“提交”即表示你的反馈将用于改进 Microsoft 产品和服务。 你的 IT 管理员将能够收集此数据。 隐私声明。

谢谢您的反馈!

×