Microsoft Visual C++ 2010 Express

■UDPプロトコルでクライアント/サーバー通信 Prev Top Next
関連ページ:なし


今回はUDPプロトコルを使用し、クライアント/サーバーモデルで通信してみます。処理方法は前回と同じで通信方式を変更しています。
さてUDPの特徴であるパケットの欠損や順序の逆転などの現象は確認していません。ルータ越えして通信を行うとこの減少が発生するらしいですが、 そこまでやるつもりはありません。

ではソースを見ていきますが、最初にいくつか注意点。 前回の記事と同じことを書きますが、ネットワーク系のサンプルソースはマルチバイト文字セットを使用します。 ですのでプロジェクトのプロパティの文字セットを変更してください。

次にこのページのサンプルではサーバー側のプログラムに 50000番 のポート番号を割り当てています。 実行中の他のプログラムが使用中の場合は 50000番 をあけるか、またはプログラムを修正して未使用の別のポート番号を割り当ててください。

---main.cpp ( サーバー側 )---


#include <stdio.h>
#include <WINSOCK2.h>

#pragma comment( lib, "WS2_32.lib" )

// アプリケーションが要求するバージョン
#define MAJOR_VERSION 2
#define MINOR_VERSION 0
// ポート番号
#define PORT         50000
// データ量の最大値は SO_MAX_MSG_SIZE
#define MESSAGE_SIZE  1024

int APIENTRY WinMain( HINSTANCE hInstance,
                      HINSTANCE /*hPrevInstance*/,
                      LPTSTR    /*lpCmpLine*/,
                      INT       /*nCmdShow*/ )
{
   int hr = -1;

   // WSAStartup() の呼び出し回数
   int WSAStartupCount = 0;

   // ソケット識別子
   SOCKET SocketID = INVALID_SOCKET;

   // 送受信バッファ
   char SendBuffer[MESSAGE_SIZE], RecvBuffer[MESSAGE_SIZE];

   // デバッガ出力用バッファ
   TCHAR OutString[512];

   // WS2_32.dllの初期化
   {
      WSADATA wsd;
   
      // WS2_32.dllの初期化
      hr = WSAStartup( MAKEWORD( MAJOR_VERSION, MINOR_VERSION ), &wsd );
      if( hr != 0 )
      {
         sprintf_s( OutString, "WSAStartup()でエラー [ ErrorCode:%d ]\n", hr );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
      WSAStartupCount++;

      // WSADATA::wVersion には、WSAStartup() に指定したバージョン以下でライブラリがサポートする最大バージョンが格納される
      // ここでは完全に一致する場合のみ動作するようにする
      if( LOBYTE( wsd.wVersion ) != MAJOR_VERSION ||
          HIBYTE( wsd.wVersion ) != MINOR_VERSION )
      {
         sprintf_s( OutString,
                    "ライブラリがサポートするバージョンは[ %d.%d ]ですがアプリが要求するバージョンは[ %d.%d ]です\n",
                    LOBYTE( wsd.wVersion ), HIBYTE( wsd.wVersion ), MAJOR_VERSION, MINOR_VERSION );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
   }

   // ソケットの作成
   {
      SOCKADDR_IN SocketAddr;

      // ソケットの作成
      SocketID = WSASocket( AF_INET,
                            SOCK_DGRAM,    // 非接続型ソケット
                            IPPROTO_UDP,   // UDPプロトコルを指定する
                            NULL,
                            0,
                            0
                          );
      if( SocketID == INVALID_SOCKET )
      {
         sprintf_s( OutString, "WSASocket()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }

      ::ZeroMemory( &SocketAddr, sizeof(SOCKADDR_IN) );
      SocketAddr.sin_family = AF_INET;
      SocketAddr.sin_addr.s_addr = htonl(INADDR_ANY);
      SocketAddr.sin_port = htons( PORT );

      // すべてのIPアドレスとポート番号を関連付ける
      hr = bind( SocketID,
                 (SOCKADDR*)&SocketAddr,
                 sizeof(SocketAddr)
               );
      if( hr != 0 )
      {
         sprintf_s( OutString, "bind()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
   }

   OutputDebugString( "UDP通信開始!!\n" );

   while( true )
   {
      RecvBuffer[0] = '\0';
      SendBuffer[0] = '\0';

      SOCKADDR_IN SockAddr;
      int AddrSize = sizeof(SockAddr);

      // クライアントから受信
      {
         WSABUF buf;
         buf.buf = RecvBuffer;
         buf.len = MESSAGE_SIZE;

         DWORD RecvBytes = 0;
         
         // データグラムを受信して送信元のアドレスを保存する
         hr = WSARecvFrom( SocketID,
                           &buf,
                           1,
                           &RecvBytes,
                           0,
                           (SOCKADDR*)&SockAddr,  // 送信元情報
                           &AddrSize,
                           NULL,
                           NULL
                         );
         if( hr != 0 )
         {
            sprintf_s( OutString, "WSARecvFrom()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
            OutputDebugString( OutString );
            hr = -1;
            goto EXIT;
         }
      }

      // クライアントへ送信
      {
         sprintf_s( SendBuffer, "-%s", RecvBuffer );
         WSABUF buf;
         buf.buf = SendBuffer;
         buf.len = MESSAGE_SIZE;

         DWORD SendBytes = 0;

         // データグラムを送信する
         hr = WSASendTo( SocketID,
                         &buf,
                         1,
                         &SendBytes,
                         0,
                         (SOCKADDR*)&SockAddr,  // 送信元情報
                         AddrSize,
                         NULL,
                         NULL
                      );
         if( hr != 0 )
         {
            sprintf_s( OutString, "WSASendTo()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
            OutputDebugString( OutString );
            hr = -1;
            goto EXIT;
         }
      }

      SYSTEMTIME st;
      ::GetLocalTime( &st );
      sprintf_s( OutString, 
                 "%d:%d:%d:%d [ 受信:%s ][ 送信:%s ]\n",
                 st.wHour, st.wSecond, st.wMinute, st.wMilliseconds, RecvBuffer, SendBuffer );
      OutputDebugString( OutString );
   }

   hr = 0;

EXIT:
   // ソケットの切断
   if( SocketID != INVALID_SOCKET )
   {
      if( closesocket( SocketID ) != 0 )
      {
         sprintf_s( OutString, "closesocket()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
      }
   }

   // WS2_32.dllの開放
   for( int i=0; i<WSAStartupCount; i++ )
   {
      if( WSACleanup() != 0 )
      {
         sprintf_s( OutString, "WSACleanup()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
      }
   }

   return hr;
}

---main.cpp ( クライアント側 )---

#include <stdio.h>
#include <WINSOCK2.h>

#pragma comment( lib, "WS2_32.lib" )

// アプリケーションが要求するバージョン
#define MAJOR_VERSION 2
#define MINOR_VERSION 0
// 接続先のサーバーのIPアドレス。127.0.0.1は特殊なアドレスで自分自身を示す。
// サンプルでは1台のPC上でテストしたため、このIPアドレスを指定する。
#define IP           "127.0.0.1"
// ポート番号
#define PORT         50000
// データ量の最大値は SO_MAX_MSG_SIZE
#define MESSAGE_SIZE  1024

int APIENTRY WinMain( HINSTANCE hInstance,
                      HINSTANCE /*hPrevInstance*/,
                      LPTSTR    /*lpCmpLine*/,
                      INT       /*nCmdShow*/ )
{
   int hr = -1;

   // WSAStartup() の呼び出し回数
   int WSAStartupCount = 0;

   // ソケット識別子
   SOCKET SocketID = INVALID_SOCKET;

   // 送受信バッファ
   char SendBuffer[MESSAGE_SIZE], RecvBuffer[MESSAGE_SIZE];

   // デバッガ出力用バッファ
   TCHAR OutString[512];

   // WS2_32.dllの初期化
   {
      // WSADATA 構造体
      WSADATA wsd;
   
      // WS2_32.dllの初期化
      hr = WSAStartup( MAKEWORD( MAJOR_VERSION, MINOR_VERSION ), &wsd );
      if( hr != 0 )
      {
         sprintf_s( OutString, "WSAStartup()でエラー [ ErrorCode:%d ]\n", hr );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
      WSAStartupCount++;

      // WSADATA::wVersion には、WSAStartup() に指定したバージョン以下でライブラリがサポートする最大バージョンが格納される
      // ここでは完全に一致する場合のみ動作するようにする
      if( LOBYTE( wsd.wVersion ) != MAJOR_VERSION ||
          HIBYTE( wsd.wVersion ) != MINOR_VERSION )
      {
         sprintf_s( OutString,
                    "ライブラリがサポートするバージョンは[ %d.%d ]ですがアプリが要求するバージョンは[ %d.%d ]です\n",
                    LOBYTE( wsd.wVersion ), HIBYTE( wsd.wVersion ), MAJOR_VERSION, MINOR_VERSION );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
   }

   // ソケットの作成
   {
      // ソケットの作成
      SocketID = WSASocket( AF_INET,
                            SOCK_DGRAM,    // 非接続型ソケット
                            IPPROTO_UDP,   // UDPプロトコルを指定する
                            NULL,
                            0,
                            0
                          );
      if( SocketID == INVALID_SOCKET )
      {
         sprintf_s( OutString, "WSASocket()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
         hr = -1;
         goto EXIT;
      }
   }

   OutputDebugString( "UDP通信開始!!\n" );

   while( true )
   {
      RecvBuffer[0] = '\0';
      SendBuffer[0] = '\0';

      SOCKADDR_IN SockAddr;
      int AddrSize = sizeof(SockAddr);

      // サーバーへ送信
      {
         // 送信データを作成
         static DWORD data = 0;
         sprintf_s( SendBuffer, "%d", data );
         data ++;

         WSABUF buf;
         buf.buf = SendBuffer;
         buf.len = MESSAGE_SIZE;

         DWORD SendBytes = 0;

         // 送信先のIPアドレスとポート番号を設定
         SockAddr.sin_family = AF_INET;
         SockAddr.sin_addr.s_addr = inet_addr( IP );
         SockAddr.sin_port = htons( PORT );

         // データグラムを送信する
         hr = WSASendTo( SocketID,
                         &buf,
                         1,
                         &SendBytes,
                         0,
                         (SOCKADDR*)&SockAddr,   // 送信先情報
                         AddrSize,
                         NULL,
                         NULL
                      );
         if( hr != 0 )
         {
            sprintf_s( OutString, "WSASendTo()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
            OutputDebugString( OutString );
            hr = -1;
            goto EXIT;
         }
      }

      // サーバーから受信
      {
         WSABUF buf;
         buf.buf = RecvBuffer;
         buf.len = MESSAGE_SIZE;

         DWORD RecvBytes = 0;

         // データグラムを受信して送信元のアドレスを保存する
         hr = WSARecvFrom( SocketID,
                           &buf,
                           1,
                           &RecvBytes,
                           0,
                           (SOCKADDR*)&SockAddr,   // 送信先情報
                           &AddrSize,
                           NULL,
                           NULL
                        );
         if( hr != 0 )
         {
            sprintf_s( OutString, "WSARecvFrom()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
            OutputDebugString( OutString );
            hr = -1;
            goto EXIT;
         }
      }

      SYSTEMTIME st;
      ::GetLocalTime( &st );
      sprintf_s( OutString,
                 "%d:%d:%d:%d [ 送信:%s ][ 受信:%s ]\n",
                  st.wHour, st.wSecond, st.wMinute, st.wMilliseconds, SendBuffer, RecvBuffer );
      OutputDebugString( OutString );
   }

   hr = 0;

EXIT:
   // ソケットの切断
   if( SocketID != INVALID_SOCKET )
   {
      if( closesocket( SocketID ) != 0 )
      {
         sprintf_s( OutString, "closesocket()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
      }
   }

   // WS2_32.dllの開放
   for( int i=0; i<WSAStartupCount; i++ )
   {
      if( WSACleanup() != 0 )
      {
         sprintf_s( OutString, "WSACleanup()でエラー [ ErrorCode:%d ]\n", WSAGetLastError() );
         OutputDebugString( OutString );
      }
   }

   return hr;
}

サーバー側アプリを実行してからクライアント側アプリを実行してください。
またウィンドウは作成していません。実行結果はデバックウィンドウに表示されます。


Prev Top Next
inserted by FC2 system