使用 Microsoft 登入
登入或建立帳戶。
您好:
選取其他帳戶。
您有多個帳戶
選擇您要用來登入的帳戶。

摘要

有時候,您必須傳送緊急的帶外(OOB)資料給您的接收器。 接收器是任何使用者或接收資料的任何應用程式。 您想要將此 OOB 資料視為比您可能傳送的任何一般資料都是更高優先順序的資料。 如果您想要傳送的 OOB 資料是一個位元組,您可以使用 select 函數來尋找 OOB 資料。 您可以使用 [ 接收 ] 函數來讀取資料。不過,在傳輸控制通訊協定(TCP)中,OOB 資料區塊永遠是一個位元組。 因此,如果您傳送多位元組的 OOB 資料,只會檢索 OOB 資料的最後一個位元組。 剩餘的資料就會視為一般資料。本文使用範例程式碼來描述如何使用 Microsoft Windows Socket (Winsock)傳送多位元組 OOB 資料。

本文內容

簡介

本文說明如何使用 Winsock 傳送多個位元組的帶外資料(chars)。 範例應用程式是在 Microsoft Visual c + + 中建立的。因為在 Microsoft Windows 通訊端層級不會直接支援此 OOB 機制,所以您必須在應用程式層級執行此 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 .cpp 回到頂端

建立伺服器應用程式

  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)顯示回到頂端

顯示用戶端應用程式的範例代碼,Myclient .cpp

注意: 您必須包含對 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 以啟動用戶端。 注意: 預留位置 [ IPAddress ] 是伺服器 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 管理員將能夠收集這些資料。 隱私權聲明。

感謝您的意見反應!

×