メイン コンテンツへスキップ
サポート
Microsoft アカウントでサインイン
サインインまたはアカウントを作成してください。
こんにちは、
別のアカウントを選択してください。
複数のアカウントがあります
サインインに使用するアカウントを選択してください。

概要

場合によっては、受信者に緊急の帯域外 (OOB) データを送信する必要があります。 受信者は、データを受信するユーザーまたはアプリケーションです。 この OOB データは、送信可能な通常のデータよりも優先度の高いデータとして扱う必要があります。 送信する OOB データが1バイトの場合は、 select 関数を使って oob データを検索できます。 Recv関数を使ってデータを読み取ることができます。ただし、伝送制御プロトコル (TCP) では、OOB データブロックは常に1バイトです。 そのため、複数バイトの OOB データを送信する場合は、OOB データの最後のバイトのみが取得されます。 残りのデータは、通常データと同じように扱われます。この記事では、Microsoft Windows Socket (Winsock) を使って複数バイトの OOB データを送信する方法を説明するサンプルコードを使用します。

この資料の内容

はじめに

この記事では、Winsock を使用して、複数バイト (char) の帯域外データを送信する方法について説明します。 サンプルアプリケーションは、Microsoft Visual C++ で作成されます。この OOB 機構は、Microsoft Windows socket レベルでは直接サポートされていないため、アプリケーションレベルでこの OOB メカニズムを実装する必要があります。 これは、実際の OOB データではありません。 このサンプルアプリケーションでは、クライアント側 (送信側とも呼ばれます) の2つのソケットを作成して、OOB データと通常データを送信します。 サーバー側 (受信側とも呼ばれます) では、2つのソケットを使って2つのスレッドのデータを処理します。 1つのスレッドは OOB データを対象としています。 もう1つのスレッドは、標準データを対象としています。 OOB 機構をシミュレートするには、これら2つのスレッドを同期する必要があります。 OOB データを処理するスレッドが、通常のデータを処理するスレッドよりも優先順位が高いことを確認してください。注: このサンプルアプリケーションでは、複数バイトの OOB データを送信する方法の1つについて説明します。 コードを環境で動作させるために、コードを変更する必要がある場合があります。先頭に戻る

クライアントアプリケーションを作成する

  1. クライアントアプリケーションを作成します。 このクライアントアプリケーションでは、次のサンプルコードでは、2つのソケットを作成する方法について説明します。

    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 のサンプルコードを表示します。 back to the top

サーバーアプリケーションを作成する

  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 機構をシミュレートするには、これら2つのスレッドを同期する必要があります。 このサンプルアプリケーションでは、グローバル イベント オブジェクトを使ってこの操作を実行します。 ファイルの先頭に次のステートメントを入力します。

    //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関数をもう一度呼び出して、ネットワークに接続されているデータがあるかどうかを確認します。

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

      読み取り待ちのデータがあるかどうかに応じて、次のいずれかの方法を使用します。

      • 読み取り待ちのデータがない場合は、 SetEvent 関数を呼び出して、指定されたイベントオブジェクトをシグナル状態に設定します。

        SetEvent(hRecvEvent);

        この操作を行うと、通常のデータハンドラーのスレッドが再開されます。 通常のデータハンドラースレッドは、通常のデータハンドラースレッドが再開される前に、 Hrecvevent イベントオブジェクトを待機します。

      • データの読み取り待ちのままの場合は、 recv 関数をもう一度呼び出して残りのデータを読み取ります。 次に、保留中のデータをもう一度確認します。

    保留中のすべてのデータが読み込まれるまで手順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 のサンプルコードを表示します。

注: コードをコンパイルするには、Winsock ライブラリファイル (Ws2_32) への参照を指定する必要があります。

#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) への参照を指定する必要があります。

#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]: ccccccの cc [Normal]: gggggggggg [normal]: [Normal]: iiiiiiiiii先頭に戻る

関連情報

詳細については、次の Microsoft 開発者ネットワーク (MSDN) Web サイトを参照してください。

プロトコルに依存しない帯域外データHttp://msdn2.microsoft.com/en-us/library/ms740102.aspxWinsock 関数http://msdn2.microsoft.com/en-us/library/ms741394.aspx関連情報を参照するには、次のマイクロソフト サポート技術情報番号をクリックしてください。

331756 Ioctlsocket 関数はインラインの帯域外データを検出できません先頭に戻る

ヘルプを表示

その他のオプションが必要ですか?

サブスクリプションの特典の参照、トレーニング コースの閲覧、デバイスのセキュリティ保護方法などについて説明します。

コミュニティは、質問をしたり質問の答えを得たり、フィードバックを提供したり、豊富な知識を持つ専門家の意見を聞いたりするのに役立ちます。

この情報は役に立ちましたか?

言語の品質にどの程度満足していますか?
どのような要因がお客様の操作性に影響しましたか?
[送信] を押すと、Microsoft の製品とサービスの改善にフィードバックが使用されます。 IT 管理者はこのデータを収集できます。 プライバシーに関する声明。

フィードバックをいただき、ありがとうございます。

×