Client と Server でゲーム情報の送受信

DOS モードで Server に接続して、じゃんけんゲームに必要な情報を送受信します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

Server プロジェクト

  1. Server 側のプログラムの手順です。
    1. Winsock を初期化する。
    2. ソケットを作る。
    3. IPアドレスとポートを設定する。
    4. ソケットをポートに bind する。
    5. Client からの接続を待つループに入る。
    6. accept() で接続を確認する。
    7. Client からの COMMAND を受け取る。
    8. COMMAND に対応したメッセージを送り返す。
    9. closesocket() でソケットを閉じる。
    10. ループから出ると Winsock を閉じて終了する。
  2. Win32 コンソールアプリケーションを作成して Server の main.cpp をプロジェクトに追加します。
  3. winsock2.h をインクルードして ws2_32.lib をリンクしなければならないのですが #pragma でリンクするので特に操作は不要です。
  4. プロジェクトをビルドして Server の Main.exe を作成して下さい。


Client プロジェクト

  1. Client 側のプログラムの手順です。
    1. Winsock を初期化する。
    2. 以下の10個のテスト用の COMMAND を Server に送ってメッセージを受け取って表示する。
      char msg[10][8]=
      { "HELLO", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "END" };
    3. COMMAND を Server に送信する手順です。
      ソケットは毎回作成しなければならないようです。
      1. ソケットを作る。
      2. 接続相手(IP アドレスとポート)を設定する。
      3. Server に接続する。
      4. COMMAND を送信する。
      5. Server からデータを受信する。
      6. 受信したデータを表示する。
      7. ソケットをクローズする。
  2. Win32 コンソールアプリケーションを作成して Client の main.cpp をプロジェクトに追加します。
  3. winsock2.h をインクルードして ws2_32.lib をリンクしなければならないのですが #pragma でリンクするので特に操作は不要です。
  4. プロジェクトをビルドして Client の Main.exe を作成して下さい。

プログラムのテスト

  1. 最初に Server の Main.exe を起動します。
    Server が起動されると次のメッセージを表示して、TCP Client からの接続待ちの状態に入ります。
    Client の接続を待っています
  2. Server の起動を確認して Client の Main.exe を起動します。
  3. Client から Server に対して10個の COMMAND を順番に送信します。
  4. Server は送られてきた COMMAND に対応するメッセージを送り返します。
  5. Client は返されたメッセージを表示します。
  6. Client は表示確認のために getch() でキーのタイプを待ちます。
    何かキーがタイプされると終了します。
  7. Server を終了させる時は、ウインドウを×ボタンで閉じて下さい。

Server プログラムの説明

  1. WSAStartup() 関数で Winsock を初期化して、socket() でソケットを作成します。
        WSADATA     wsaData;
        SOCKET      sock0;
        SOCKET      sockw;
        struct      sockaddr_in addr;
        struct      sockaddr_in client;
        char        buf[1024];
        char        msg[40];
        char        comd[3][8]= { "HELLO", "PLAY", "END" };
    
        int main()
        {
            int     len;
            int     n;
    
            // winsock2の初期化
            if (WSAStartup(MAKEWORD(2,0), &wsaData))
            {   puts("winsock の初期化に失敗しました");
                getch();
                return -1;
            }
    
            // ソケットの作成
            sock0= socket(AF_INET,SOCK_STREAM,0);
            if (sock0 == INVALID_SOCKET)
            {   printf("socket : %d\n", WSAGetLastError());
                getch();
                return -1;
            }
        
  2. sockaddr_in にIPアドレスとポートを設定して、bind() でソケットをポートに bind します。
    IPアドレスは INADDR_ANY に、ポート番号 12345 に設定しています。
    listen() で Client からの接続要求を待ちます。
    (sock0, 5) の5は、接続を受け付けるキューの最大数です。
            // ソケットの設定
            addr.sin_family = AF_INET;
            addr.sin_port = htons(12345);
            addr.sin_addr.S_un.S_addr = INADDR_ANY;
            // ソケットをバインド
            if (bind(sock0,(struct sockaddr *)&addr,sizeof(addr)) != 0)
            {   printf("bind : %d\n", WSAGetLastError());
                getch();
                return -1;
            }
    
            // TCPクライアントからの接続要求を待てる状態にする
            if (listen(sock0, 5) != 0)
            {   printf("listen : %d\n", WSAGetLastError());
                getch();
                return -1;
            }
    
            puts("Client の接続を待っています");
        
  3. Server は役目が終わるまで Client からの接続待ちで待機します。
    Server はウインドウの×ボタンで強制的に終了させるので WSACleanup() が実行されることはありません。
    accept() で Client からの接続を確認してソケットを受け取ります。
    recv() で Client からの COMMAND を受信します。
    Client の COMMAND を解析して対応するメッセージを send() で送信します。
    Client から受け取ったソケットを閉じて、次の接続に備えます。
            while(1)
            {   len = sizeof(client);
                sockw = accept(sock0, (struct sockaddr *)&client, &len);
                if (sockw == INVALID_SOCKET)
                {   printf("accept : %d\n", WSAGetLastError());
                    break;
                }
    
                // 送られてきたメッセージ(COMMAND)を受け取ります
                memset(buf,0,1024);
                recv(sockw,buf,1024,0);
                if (buf[0]=='\0')   strcpy(buf,"NULL");
                puts(buf);
    
                // COMMAND を解析して、メッセージを送信します
                switch(buf[0])
                {   case 'H': case 'h':
                        strcpy(msg,"HELLO");
                        break;
                    case 'P': case 'p':
                        sprintf(msg,"%d",rand()%3);
                        break;
                    case 'E': case 'e':
                        strcpy(msg,"BYE");
                        break;
                    default:
                        strcpy(msg,"ERROR");
                }
    
                n = send(sockw, msg, strlen(msg), 0);
                if (n < 1)
                {   printf("send : %d\n", WSAGetLastError());
                    break;
                }
                closesocket(sockw);
            }
            closesocket(sock0);
            WSACleanup();
            return 0;
        }
        
  4. Client の COMMAND と送り返すメッセージです。
    1. HELLO COMMAND
      HELLO を送る
    2. PLAY COMMAND
      グー/チョキ/パーを乱数で選択して送る
    3. END COMMAND
      BYE を送る

Client プログラムの説明

  1. WSAStartup() 関数で Winsock を初期化します。
        WSADATA     wsaData;
        struct      sockaddr_in addr;
        SOCKET      sockw;
        char        msg[10][8]=
        { "HELLO", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "PLAY", "END" };
    
        int main()
        {
            char    buf[40];
            int     n,i;
    
            // winsock2の初期化
            if (WSAStartup(MAKEWORD(2,0), &wsaData))
            {   puts("winsock の初期化に失敗しました");
                getch();
                return -1;
            }
        
  2. 送信する COMMAND の数だけループします。
    ループの中で、毎回ソケットを作成します。
            // Server に connect して COMMAND を送信する
            for(i=0; i<10; i++)
            {   // ソケットの作成
                sockw= socket(AF_INET,SOCK_STREAM,0);
                if (sockw == INVALID_SOCKET)
                {   puts("ソケットの作成に失敗しました");
                    getch();
                    return -1;
                }
        
  3. sockaddr_in に Serber のIPアドレスとポートを設定して connect() で接続を要求します。
    接続が出来ると COMMAND を送信します。
                // 接続先指定用構造体の準備
                addr.sin_family = AF_INET;
                addr.sin_port = htons(12345);
                addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
                // サーバーに接続
                if (connect(sockw, (struct sockaddr *)&addr, sizeof(addr)))
                {   puts("connect に失敗しました");
                    getch();
                    return -1;
                }
    
                // Server にリクエスト(COMMAND)を送信
                n= send(sockw, msg[i], strlen(msg[i]), 0);
                if (n < 0)
                {   puts("send に失敗しました");
                    getch();
                    return -1;
                }
        
  4. Serber から COMMAND に対応したメッセージが送られてきます。
    受信したメッセージを画面に表示します。
    毎回ソケットをクローズします。
                // サーバーからデータを受信
                memset(buf,0,40);
                n = recv(sockw,buf,40,0);
                if (n<0)
                {   puts("recv に失敗しました");
                    getch();
                    return -1;
                }
                printf("%d,%s\n",n,buf);
    
                // ソケットのクローズ
                closesocket(sockw);
            }
        
  5. 10個の COMMAND のテストが終われば終了です。
    Winsock を閉じて、表示を確認するために getch() で待機します。
    何かキーがタイプされれば終了です。
            // winsock2 の終了処理
            WSACleanup();
            getch();
            return 0;
        }
        

超初心者のプログラム入門(Win32API C++)