トップページ
ひらく | たたむ | ページトップ
↓マウスで反転選択した文字を検索
Winsock
   
ページ内検索 ページ外検索
検索したい文字を入力して
ENTERを押すと移動します。
\n
[ トップページ ]
[ ____CommandPrompt ] [ ____JScript ] [ ____MySQL ] [ ____Cygwin ] [ ____Java ] [ ____Emacs ] [ ____Make ] [ ____Perl ] [ ____Python ] [ ____OpenGL ] [ ____C# ] [ ____StyleSheet ] [ ____C++ ] [ ____Winsock ] [ ____Thread ] [ ____VisualStudio ] [ ____C ] [ ____Win32API ] [ ____Lua ] [ ____PhotoShop ]
ヘッダ検索
___

■ 使い方


___

■ WinSockとは

ウィンドウズでソケットを使ってプログラムを書くための API TCP/IPなどインターネットを使って通信を行うプログラムが書ける NetworkService( API ) 最初 WinSock は、TCP/IP プロトコルのために開発されましたがその後も バージョンアップを重ね、 現在ではプロトコルに依存しないWinSock 2 が主流として使われてます SocketAPI を利用すると TCP/IP の詳細な処理は気にしなくてもいい Socket という相手への プラグを経由して Message のやりとりをすればいいから ラクチン !
___

■ NetworkProgram の骨格

POINT ネットワーク上で情報を通信しあうためには相手と自分という最低 2つのProgramが必要になる。 先に起動して相手の接続を待っているプログラムと 後からサーバに接続を試みるプログラムの2つ ネットワークアプリケーションというのは、クライアントソフトとサーバーソフト これら二つがそろって、完成されたサービスとなる TCP を使った通信プログラムの書き方 は サーバ と クライアントで異なる。 POINT
    一度接続できれば、サーバとクライアントで通信方法に同じ
POINT POP3 や FTP、HTTP のようなプログラムを作るには、ソケットの知識だけではなく プロトコルの仕様も知る必要がある DESC NetworkProgram で用意するのは [ ServerProgram ] と [ ClientProgram ] 次のように 2 つの Program をつくる
         サーバー                 クライアント
           ↓                        ↓
       WSAStartup               WSAStartup
           ↓                        ↓
         socket 接続待ちをする     socket
                IP, ポートを設定             
           ↓                        |
          bind                       |
           ↓                        |
         listen                      ↓
           ↓     ←―――――    connect   // 相手( 接続待ちのServer )を IP, Port で指定
         accept   ―――――→       |
           ↓                        ↓
       send/recv  ←――――→   send/recv  // 通信する
           ↓                        ↓
       closesocket               closesocket
           ↓                        ↓
       WSACleanup                WSACleanup
サーバーはクライアントからの接続を待ちうけ 通信用にソケットを生成して通信する Server と Client がやりとりするためのおおまかな流れ
    1. まず Server を起動してたちあげておく
       Server が自分で決めた Port(入り口)をあけてリクエスト待ちをする

    2. サービスをうけたいときに Client を実行する
    Client をたちあげて マシン名と Port を指定して通信を開始する
POINT ClientProgram は ServerProgram の [ HostName ] , [ IP ]を知っている必要がある
___

■ winsock.の初期化

     ws2_32.lib
     #include < winsock2.h> 

    // version ごとの対応はこれ
    // version           DLL           ヘッダ      ライブラリ
    Winsock1.1 (16bit)  WINSOCK.DLL  winsock.h  winsock.lib
    Winsock1.1 (32bit)  WSOCK32.DLL  winsock.h  wsock32.lib
    Winsock2.0 (32bit)  WS2_32.DLL    winsock2.h  ws2_32.lib

はじめての Winsock プログラム。 DLL を初期化して終了するだけ。
   #include < winsock2.h>


   int main()
   {
    WSADATA wsaData;

    // Major , Minor -> WORD 型を pack するマクロ. 
    // typedef unsigned short      WORD;
    // wsaData に格納される. 
    WSAStartup( MAKEWORD(2,0), &wsaData );

    // 終了処理.
    WSACleanup();

    return 0;
   }  
エラー処理を追加してみる。
   #include < stdio.h>
   #include < winsock2.h>

   int main()
   {
    WSADATA wsaData;
    int err;

    err = WSAStartup( MAKEWORD(2,0), &wsaData );
    if (err != 0) {
     switch (err) {
       // ネットワークサブシステムがネットワークへの接続を準備できていない
       case WSASYSNOTREADY:
         printf("WSASYSNOTREADY\n");
         break;
       // 要求されたwinsockのバージョンがサポートされていない
       case WSAVERNOTSUPPORTED:
         printf("WSAVERNOTSUPPORTED\n");
         break;
       //   winsockが処理できる最大プロセス数に達した
       case WSAEINPROGRESS:
         printf("WSAEINPROGRESS\n");
         break;
       // 第二引数であるlpWSAData は有効なポインタではない
       case WSAEPROCLIM:
         printf("WSAEPROCLIM\n");
         break;
       case WSAEFAULT:
         printf("WSAEFAULT\n");
         break;
     }
    }

    WSACleanup();

    return 0;
   }
___

■ シンプルサーバー(SimpleServer)

SAMPLE シンプルサーバー ポート7777 を利用 シンプルサーバー ポート7777 を利用 GUI DESC 一般的な TCP を使ったサーバは、複数回クライアントからの接続を受ける ( 接続が複数である点に注意. while(1){} の部分が複数する部分. ) POINT
    接続要求をうける Code を while (1) で繰りかえすこと
  #include < stdio.h>
  #include < winsock2.h>

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

  int main()
  {
   WSADATA wsaData;
   WSAStartup(MAKEWORD(2,0), &wsaData);

   SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

   struct sockaddr_in adr;
   adr.sin_family = AF_INET;
   adr.sin_port = htons(7777);
   adr.sin_addr.S_un.S_addr = INADDR_ANY;  // サーバ側で割り当てられている IP を自動で設定


   // Socket へアドレスを設定
   bind(sock, (struct sockaddr *)&adr, sizeof(adr));


   listen( sock, 5 );


   // ここで 受付を繰り返す
   while (1) {

     struct sockaddr_in client;
     int len = sizeof(client);

     // 接続要求を受け付ける。
     // スレッドはブロックされる( GUI では MainThread で呼ばないこと ) 
     printf( "クライアントからの接続をまってます\n" );
     SOCKET sockCli = accept(sock, (struct sockaddr *)&client, &len);


     // クライアントへ送信する 
     printf( "クライアントからの接続を受け付けました。メッセージを送信します\n" );
     const char *msg = "HELLO\n";
     send(sockCli, msg, strlen(msg), 0);


     // 接続解除
     // クライアントとのセッションを終了する

     // こちら側の通信を切る
     // [ Client ] < ---------- [ Server ] 
     closesocket( sockCli );
   }

   WSACleanup();

   return 0;
  }

POINT 先にサーバープログラムを起動して待機させておく。 次に好きなタイミングでクライアントを起動する。
    # Server を起動しておく
    shell> ./simplesrv.exe

    # telnet で接続する
    shell> telnet localhost 7777
___

■ シンプルクライアント(SimpleClient)

SAMPLE シンプル クライアント port 7777 へ接続 シンプル クライアント port 7777 へ接続 GUI クライアントプログラムはサーバがリスンしているポートへコネクトする。 接続後のやりとりの手順( プロトコル )はネットワークアプリケーションの次第。 単にサーバから "HEELO" の文字だけもらって終了する。

    int main(int argc, char *argv[])
    {
      WSADATA wsaData;
      char buf[32];


      WSAStartup(MAKEWORD(2,0), &wsaData);

      // TCP ソケットの作成
      SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);


      // 送信先のサーバのアドレスを設定
      struct sockaddr_in adr;
      adr.sin_family = AF_INET;
      adr.sin_port = htons(7777);

      // POINT
      //    dot 表記 | DNS の名前がくる可能性がある.  

      // IPv4 ( dot 表記 )から取得
      char *ip = "localhost";
      adr.sin_addr.S_un.S_addr = inet_addr( ip );

      // 無効だったので, DomainName から検索する
      if (adr.sin_addr.S_un.S_addr == 0xffffffff) {
        struct hostent *host;

        // 名前( DNS がつけた名前 )から IP をひく 
        host = gethostbyname( ip );
        if (host == NULL) {
          return 1;
        }
        adr.sin_addr.S_un.S_addr =

          // host から IP( 32 bit )を取得可能.
          *(unsigned int *)host->h_addr_list[0];
      }


      // 接続. -> server::accept() へ
      connect( sock, (struct sockaddr *)&adr, sizeof(adr) );

      memset(buf, 0, sizeof(buf));
      int n = recv(sock, buf, sizeof(buf), 0);

      printf("%d, %s\n", n, buf);

      closesocket(sock);

      WSACleanup();

      return 0;
    }
___

■ accept.した相手を表示するサンプル

DESC accept の第二引数は、接続した相手に関する情報( IP, port... )を含む IPアドレスの表示には inet_ntoa を利用する. inet_ntoa は、引数として渡した struct in_addr を表現する文字列を返す.
   printf(

      "accepted connection from %s, port=%d\n",
            // 32 bit Addr を 文字列に変換.
            inet_ntoa( client.sin_addr ), 

            // unsigned short の値を host の ByteOrder に変換.
            ntohs( client.sin_port )
          );
___

■ エコーサーバー

SAMPLE シンプル エコークライアント port 7777 へ接続 GUI シンプル エコーサーバ port 7777 でリスン GUI
___

■ バイナリファイルの取得

SAMPLE ファイルをダウンロードして %HOMEPATH%test.bmp として保存 HTTP プロトコルを利用して、バイナリファイルを取得する。 テキストファイルを取得する場合と処理はほとんど同じだが、 バイナリデータは \0 を含むケースがあるためその点だけ注意する。
      // 同じように接続する。
      int ret = connect( sock, (struct sockaddr *)&adr, sizeof(adr) );

      // バイナリファイルを取得するHTTPメッセージを送信する。
      const char *msg = "GET /bin/test.bmp HTTP/1.0\r\nHost: ooo.iiyudana.net\r\n\r\n";
      send( sock, msg, strlen(msg), 0 );


      // 受信バッファ
      char *p = new char[1024*1024*10];
      
      int len = 0;
      while (1) {

        // すべてのデータをネットワーク先から受信するまで recv を続ける。
        ret = ::recv(sock, buf, sizeof(buf) - 1, 0 );
        buf[ret] = '\0';
        len += ret;

        // 今回の受信分をまとめる
        if ( ret ) {
          memcpy( p, buf, ret );
          p += ret;
        }

        // すべてを受信したら完了。
        if ( ret == SOCKET_ERROR || ret == 0 ) {
          break;
        }
      }

      // 受信したデータから HTTP レスポンスのヘッダ部分を除去してからファイルに書き込む。
      {
        int idx = 0;
        while (1) {
          if ( memcmp( ( p + idx ), "\r\n\r\n", 4 ) == 0 ) {
            p[idx] = '\0';
            printf( "HTTP response header\n\n%s\n\n\n", p );
            break;
          }
          idx ++;
        }


        // バイナリファイルとして書く。
        {
          const char *d = getenv( "HOMEPATH" );
          std::string path = "c:\\";
          path += d;
          path += "\\test.bmp";
          FILE *fp = fopen( path.c_str(), "wb" );
          fwrite( p + (idx+4), len - (idx + 4), 1, fp );
          fclose( fp );
        }
      }

___

■ Block.処理を回避する

DESC TCP/IP を利用した 通信プログラムでは相手からの返答のまちがはいる 問題は accept() send() recv() が処理まちになること そのため スレッド立てて、バッファ用意して何とかすることにする スレッド立てると、送信受信はいいが接続、切断がが非同期に来てメンドイ さらにサーバーで1コネクション1スレッド立ててた スレッド数 WSAEventSelect を利用 // 何かしらの NetworkEvent が起こると Event をシグナル状態にして知らせてくれる WSAWaitForMultipleEvents を使用すれば WSA_MAXIMUM_WAIT_EVENTS まで同時に待ち受けることができる
  // winnt.h 
  #define MAXIMUM_WAIT_OBJECTS 64

  // winsock2.h 
  #define WSA_MAXIMUM_WAIT_EVENTS MAXIMUM_WAIT_OBJECTS
___

■ クライアントごとにスレッドを作成

SAMPLE チャットクライアント チャットサーバー port 7777 次のよう流れで処理をする。

    while(1) {

      // クライアントのソケットを受けとる。
      SOCKET sock = accept();

      // この後に次のクライアントの相手をする必要がある。
      // そこでスレッドに accept() した相手と対話をしてもらう。
      CreateThread( ClientHandler, arg );


      // すぐに次のクライアントの待ちに入る。
    }      

    // 各クライアントとの対話処理の本体

    void ClientHandler( void *arg ) {
    
      // クライアントのソケットを受け取る
      SOCKET sock = *((SOCKET *)arg);
      free( arg );


      // エコーサーバーの処理。
      char buf[256];
      int len = recv(sock, buf, sizeof(buf), 0);
      buf[ len ] = '\0';

      // スレッドの効果を確認するため、少し寝てみる。
      Sleep( 5 * 1000 );

      send( buf, strlen(buf), 0 );
      
      closesocket( sock );
    }

    // サーバーのメインではアクセプトのみを繰り返す。
    // 受付と案内だけをする。
    void main() {

      listen();

      while (1) {
      
        // クライアントの接続を受け付ける
        SOCK sock = accept();

        SOCKET *p = malloc ( sizeof(SOCKET) );
        *p = sock;
        
        // 後のことはスレッドにまかせて
        CreateThread( 0, 0, 
                      (LPTHREAD_START_ROUTINE)ClientHandler, p, 
                      0, 0 );

        // すぐに次の客をまつ。
      }      
    }


___

■ select.を利用する

    // サーバーソケットを作成する
    SOCKET createServerSocket( unsigned short port ) 
    {


      SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

      struct sockaddr_in adr;

      adr.sin_family = AF_INET;
      adr.sin_port = htons(port);
      adr.sin_addr.S_un.S_addr = INADDR_ANY;  // サーバ側で割り当てられている IP を自動で設定

      bind(sock, (struct sockaddr *)&adr, sizeof(adr));

      listen( sock, 5 );

      return sock;
    }
    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      fd_set sets;
      int i;
      SOCKET aSock[2];

      struct timeval time = {
        3,  // 3 秒まつ
        0   // usec 単位では指定しない
      };

      // 2つのポートでリスンする。
      for( i=0; i< 2; i++ ){
        aSock[i] = createServerSocket( 1001 + i );
      } 

      while(1) {

        // リストをクリア
        // WARNING 
        //    select の実行毎にクリアする必要がある。しないと -1 が返る
        FD_ZERO( &sets );

        // 監視したいソケットをリストに追加
        for( i=0; i< 2; i++ ){
          FD_SET( aSock[i], &sets );
        }

        
        int ret = select( 10000, &sets, NULL, NULL, &time );

        if ( ret == 0 ) {
          printf( "select timeout\n" );
        }
        else {
          printf( "select ret %d \n", ret );

          for( i=0; i< 2; i++ ){
            if ( FD_ISSET( aSock[i], &sets ) ) {
              printf( "request\n" );

              struct sockaddr_in adr;
              int len;

              // これをしないと, request 待ちが常にON になるため Loop が繰り返される
              SOCKET sockCli = accept( aSock[i], (struct sockaddr *)&adr, &len );

              // クライアントとの対話を普通にすると recv などでブロックされる可能性はある。
              char buf[256];
              int n = recv(sockCli, buf, sizeof(buf), 0);
              buf[ n ] = '\0';
              printf( "client mgg %s\n", buf );
              send( sockCli, buf, strlen(buf), 0 );

              closesocket( sockCli );
            }
          } 

        }


      }
    }
___

■ select

SYNTAX int select ( int nr, // 検索するポートの値の上限 fd_set *readfds, // 入力( connect, recv ) fd_set *writefds, // 出力( send ) fd_set *exceptfds, const struct timeval *timeout // timeout 時間( NULL で永久にまつ ) ); RET N : I/O が可能になったソケットの数 0 : タイムアウトをした。 -1 : エラー DESC 指定したソケットリストのイベントをチェックして、fd_set にI/Oが可能になった ソケット情報をセットして返す。 これによってプログラム側は I/O でブロックされないと仮定して処理を続行できる。 NULL が渡された ソケットリストはチェックされない。 POINT 接続要求( connect )も入力としてみなす。
    // ソケットをリストからすべて削除する
    FD_ZERO( reads )

    // 指定したソケットをリストから削除する
    FD_CLR( reads )

    // 指定したソケットをリストへ追加する
    FD_SET( reads )

    // 指定したソケットがリストにあるかチェック
    FD_ISSET( sock, reads )

  WARNING
    select を実行毎にリセットが必要。
    しないとエラー( -1 )が返る

    // connect, recv のみをチェックする
    select( 1000, reads, NULL, NULL, NULL );
___

■ Win32のEventObjectを使う

___

■ connectのタイムアウト

POINT WSA で始まる関数は WinSock 固有の関数。 その時のタイムアウトはWinSockのとき20秒ほどになっています タイムアウト秒数を指定する関数はないので, イベントを使う。 ブロック型の Winsock 関数 ( connect()) のいくつかは タイムアウト時間は中に埋め込まれいる 理由は 正しいタイムアウト時間を設定するために必要な情報を全て もつ のは プロトコルスタックだけだから setsockopt()において SO_SNDTIMEO または SO_RCVTIMEO オプションを設定すれば send() と recv() のタイムアウトを変更できる。 しかし connect() にはない。 これ以外の解決方法は ブロック型ソケットのAPIを使用しないこと。 非ブロック型ソケットの関数は全て タイムアウトをすることが できるようになっている。

    bool connectWithTimeout( SOCKET soc, DWORD ip, unsigned short port, int time )
    {

    // nTimeout : タイムアウト時間(ms単位。1秒=1000ms)
    // 戻り値:TRUEで成功、FALSEで失敗


    WSAEVENT hEvent;
    int ret,err,retflag = FALSE;
    DWORD ret;
    WSANETWORKEVENTS events;
    sockaddr_in adr;

    // イベント作成
    h = WSACreateEvent();
    if(h == WSA_INVALID_EVENT) return false;

    // イベント型に(自動的にノンブロッキングになる)
    ret = WSAEventSelect(soc, h, FD_CONNECT);
    if(ret == SOCKET_ERROR)
    {
        WSACloseEvent(h);
        return false;
    }

    //接続
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_port = htons(port);
    adr.sin_addr.s_addr = ip;

    if(connect(soc, (sockaddr *)&adr, sizeof(adr)) == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        //WSAEWOULDBLOCKの時はまだ接続されていないので続ける
        if(err != WSAEWOULDBLOCK) goto END;
    }

    // 指定時間だけイベント発生待機
    //    時間が立っても何もなければ WSA_WAIT_TIMEOUT
    //   
    // ret が WSA_WAIT_EVENT_0以外でエラー
    ret = WSAWaitForMultipleEvents(1, &h, FALSE, time, FALSE);
    if( ret != WSA_WAIT_EVENT_0 ) goto END;




    // 発生したイベントの種類を調べる
    ret = WSAEnumNetworkEvents(soc, h, &events);
    if(ret == SOCKET_ERROR) goto END;


    // 今回はコネクト待ちをしていたので, コネクト関連のイベントがあるか調べて、
    if( (events.lNetworkEvents & FD_CONNECT) 
        && 
        // エラーがないこともチェックする。
        events.iErrorCode[FD_CONNECT_BIT] == 0) {
        ret = TRUE;
    }


    END:
    //イベント型を終了
    WSAEventSelect(soc, NULL, 0);
    WSACloseEvent(h);

    //ブロッキングに戻す(必要なら)
    ret = 0;
    ioctlsocket(soc, FIONBIO, &dwret);

    return ret;
    }


___

■ recv.timeout

    int recvWithTimeout( SOCKET sock, int time )
    {
        char  buf[1024];

        HANDLE  h = WSACreateEvent();
        WSANETWORKEVENTS    events;

        // recv, close のイベントを通知してもらうように設定。
        WSAEventSelect( sock, h, FD_READ|FD_CLOSE );

        while(1)
        {
            // 時間を指定してまつ
            int ret = WSAWaitForMultipleEvents(1, &h, FALSE, time ,FALSE);

            // 失敗
            if(ret == WSA_WAIT_FAILED)
            {
                printf("error WSAWaitForMultipleEvents\n");
                break;
            }

            // どのイベントがあったか調べる
            if(WSAEnumNetworkEvents(fd,hEvent,&events) == SOCKET_ERROR)
            {
                printf("error WSAEnumNetworkEvents\n");
            }
            else
            {
                // 相手が接続を切ったら
                if(events.lNetworkEvents & FD_CLOSE)
                {
                    printf("close\n");
                    // 自分も切って終了
                    closesocket(fd);
                    break;
                }

                // 相手からのメッセージが届いたら
                if(events.lNetworkEvents & FD_READ)
                {
                    // 受け取る
                    int len = recv(sock, buf, 1024, 0);
                    printf("Received %d bytes\n",len);
                }
            }
        }

        // イベントオブジェクト破棄
        WSACloseEvent( h );

        return 0;
    }
___

■ WSAWaitForMultipleEvents

DWORD WSAWaitForMultipleEvents( DWORD num, // イベントオブジェクト数。1 〜 WSA_MAXIMUM_WAIT_EVENTS で指定 const WSAEVENT FAR *lphEvents, // イベントオブジェクトハンドルの配列のポインタ BOOL fWaitAll, // TRUE : 全てのイベントがシグナル状態になるまで待つ。FALSE : どれか一つがシグナルのときに戻る。 DWORD time, // タイムアウト時間( msec ) BOOL fAlertable // TRUE : 関数から戻るときに I/O完了ルーチンが実行される。FALSE : 実行されない ); Return WSA_WAIT_FAILED : 失敗 // 成功した場合は次のどれかが返る // また event オブジェクトにもその内容が設定される WSA_WAIT_TIMEOUT : タイムアウトになった。 WAIT_IO_COMPLETION : 一つ以上のI/O完了ルーチンが実行のためにキューに詰まれた WSA_WAIT_EVENT_0 〜 (WSA_WAIT_EVENT_0 + cEvents - 1) fWaitAllがTRUE : 全てのイベントがシグナルになったことを表す。 FALSE : WSA_WAIT_EVENT_0を引いた戻り値はシグナル状態になった イベントハンドルのインデックスを表す
___

■ WSAResetEvent

DESC イベントのシグナル状態をリセットする。
    WSAResetEvent( events[idx - WSA_WAIT_EVENT_0]);    
___

■ WSARecv

SYNTAX int WSARecv( SOCKET s, LPWSABUF lpBuffers, // Buffer への pointer DWORD dwBufferCount, // buffer 数 LPDWORD lpNumberOfBytesRecvd, // pointer to the number, in bytes, of data received by this call if the receive operation completes immediately. If the lpOverlapped parameter is non-NULL, this parameter is optional and can be set to NULL. LPDWORD lpFlags, // pointer to flags used to modify the behavior of the WSARecv function call LPWSAOVERLAPPED lpOverlapped, // pointer to a WSAOVERLAPPED structure LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完了が終了した時点での callback ); DESC 接続ソケット(connect)からデータを受けとる 重複 Socket のみに利用する
___

■ ByteOrder

DESC Network Program でぶつかる問題 ( どんな Computer があるかわからない. ) Server は同じようにサービスをする必要がある 問題1 Endian Intel Processor が支配しているため, LE が一般的 下位 Byte からアドレスに格納. 0xABCD というワード長のデータを 0xCDAB という順でメモリに保存します Network では, BE が標準的. WinSock がこの問題を解決( 抽象化する. ) ( ie. ByteOrder を気にする必要がない. ) // とりあえず以下の 関数を使えば, ByteOrder を気にする必要ない
      htons : host to network short
      
      // ホストバイトオーダーをネットワークバイトオーダーに変換
      // 32 ビットホストバイトオーダーを
      // hostshort には 16 ビットホストバイトオーダーを指定します
      htons()
      htonl()

      // 逆.
      ntohs()
      ntohl()
*
    #include < stdio.h>
    #include < winsock2.h>

    X86 系の結果.
      DD : CC : BB : AA
      AA : BB : CC : DD

    int main() {
      unsigned int iValue = 0xAABBCCDD;
      unsigned char *pc = (char *)&iValue;

      // LE なので下位 word から順番に格納される.
      // Byte Level でメモリを若い順番からアクセスするとわかる. 
      printf("%X : %X : %X : %X\n" , pc[0] , pc[1] , pc[2] , pc[3]);

      // [ host ] -> [ network ] order へ変換. 
      // Hostが BE ならば, 何もしない. 
      iValue = htonl(iValue);
      printf("%X : %X : %X : %X\n" , pc[0] , pc[1] , pc[2] , pc[3]);
      return 0;
    }
___

■ HttpClient

SAMPLE HTTP クライアント HTTP クライアント GUI DESC Browser の簡易版 Browser とは HTTPServer に HTTP Request をなげている Program のこと TCPClient の一例として HTTP Client を作成 HTTPサーバーとのやり取りは非常に簡単 クライアントがファイルのリクエストを送信すると HTTPサーバーがヘッダーとデータを送信してきて HTTPクライアントがデータを受信し終わると 切断して セッションを終了するという流れになる メールを送信するSMTPというプロトコル( 通信方法 )との違いは サーバーとあまり対話を繰り返さないこと WARNING HTTPサーバーに接続できたらすぐリクエストを送信してあとは受信するだけ リクエストを送信する際に[ リクエストの終わりを示す改行のみの行 ]を送信しないと HTTPサーバーはそのまま待機した状態になり データを受信できなくなってしまう SYNTAX GET file PROTOCOL名/VERSION
    // 一番簡単な HTTP GET メッセージ
    GET /index.html HTTP/1.1/\r\n\r\n
GET path HTTP/1.0 NULL POINT HTTP Server が相手だと, Request を投げないと何もかえってこない 相手によっては Client から声をかけないとだめ 返ってくるデータは, HTTPサーバーから処理の結果 データのサイズ データの更新日などのヘッダー情報と、実際のデータが送信される
        HTTP/1.1 200 OK
        Server: Apache-Coyote/
        ETag: W/"116-124713615
        Last-Modified: Thu, 09
        Content-Type: text/htm
        Content-Length: 116
        Date: Thu, 09 Jul 2009
        Connection: close
    // 接続先のホストとポートを指定する
    // ウェブサーバなので ポートは 80 
    adr.sin_addr.S_un.S_addr = inet_addr("74.125.31.106");
    adr.sin_port = htons( 80 );


    // main page の HTML をリクエストする HTML メッセージ。
    const char *msg = "GET / HTTP/1.0\r\n\r\n";
    send( sock, msg, strlen(msg), 0 );


    // Web Server からの返答をもらう
    std::string s;
    while (1) {
        ret = ::recv(sockSrv, buf, sizeof(buf) - 1, 0 );
        buf[ret] = '\0';
        s += buf;

        if ( ret == SOCKET_ERROR || ret == 0 ) {
          break;
        }
    }
    printf( "%s\n", s.c_str() );

___

■ ホスト名とアドレス

DESC 接続先の Computer 識別するには名前が必要 IP がそれ. ***.***.***.*** という形式の 32 ビット( 4 byte ) で表現される識別子 でも数値ではわかりずらいので名前をつける. google.com -> google.com -> DNS -> IP [ 住所 ] と [ 郵便番号 ]の関係 1. ホスト名を使ってコンピューターの情報を取得する ( Winsock がすべて解決してくれる. )
           struct hostent FAR * gethostbyname (const char FAR * name);


  POINT
    hostent 構造体はホストの情報を格納する

   struct hostent {
       char FAR *       h_name;         // ホスト名
       char FAR * FAR * h_aliases;      // 別名
       short            h_addrtype;     // Address の種類   AF_INET 
       short            h_length;       // adr size  IP であれば 4 バイト
                                        // IPv6  6 バイト
       char FAR * FAR * h_addr_list;
   };

C:\...>test www.age-soft.co.jp 公式名 = ns.age-soft.co.jp 別名 = www.age-soft.co.jp IP = 210.143.102.133
   int main(int argc , char *argv[]) {
     LPHOSTENT host;
     WSADATA wsaData;
     int i;

     if (argc == 1) return 0;

     WSAStartup( 2 , &wsaData );
     host = gethostbyname(argv[1]);

     if (host == NULL) {
       fprintf(stderr , "ホスト名の取得に失敗しました : %s" , argv[1]);
       return 0;
     }

     printf("公式名 = %s\n" , host->h_name);
     for(i = 0 ; host->h_aliases[i] ; i++) {
       printf("別名 = %s\n" , host->h_aliases[i]);
     }

     for(i = 0 ; host->h_addr_list[i] ; i++) {
       printf("IP = %d.%d.%d.%d\n" ,
         (BYTE)*((host->h_addr_list[i])) ,
         (BYTE)*((host->h_addr_list[i]) + 1) ,
         (BYTE)*((host->h_addr_list[i]) + 2) ,
         (BYTE)*((host->h_addr_list[i]) + 3)
       );
     }

     WSACleanup();
     return 0;
   }
___

■ WinSock.固有の型

___

■ SOCKET

ソケットのインスタンスをあらわす整数の識別子 winsock.h 内で
    typedef  u_int  SOCKET;
___

■ UDP

POINT UDP Client は UDP サーバとのみ接続ができる
___

■ UDPClient


    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      // UDP 用のソケット作成
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

      // ローカルサーバのアドレスを指定
      struct sockaddr_in adr;
      adr.sin_family = AF_INET;
      adr.sin_port = htons(7777);
      adr.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" );


      // POINT
      //    TCP とは異なり, 接続処理が不要のため connect なしですぐに send() する
      char buf[] = "test";
      int len = sizeof(adr);
      // 他のソケットが使用していないポート番号が自動で設定される。
      // サーバはこのポートを見て返事を返すので特に知る必要はない
      sendto( sock, buf, strlen(buf), 0, (struct sockaddr *)&adr, len );

      {
        int len = sizeof(adr);
        char buf[256];
        printf( "recvfrom にはいります\n" );
        int n = recvfrom( sock, buf, sizeof(buf), 0, (struct sockaddr *)&adr, &len );

        printf( "echo ret = %s\n", buf );
        gets( buf );
      }

    }

___

■ UdpServer

    // サーバーソケットを作成する
    SOCKET createServerSocket( unsigned short port ) 
    {
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

      struct sockaddr_in adr;

      adr.sin_family = AF_INET;
      adr.sin_port = htons(port);
      adr.sin_addr.S_un.S_addr = INADDR_ANY;  // サーバ側で割り当てられている IP を自動で設定

      bind(sock, (struct sockaddr *)&adr, sizeof(adr));

      // 接続をする必要がないため listen() は不要

      return sock;
    }


    // UDP を使ったエコーサーバ
    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      SOCKET sock = createServerSocket( 7777 );

      // クライアントのメッセージを返すことを繰り返す
      while(1) {

        struct sockaddr_in adr;

        // WARNING 
        // この処理は必須
        int len = sizeof(adr);

        // 移行のクライアントとの対話を普通にすると当然 recv などでブロックされる。
        char buf[256];

        // POINT
        //  UDP  では接続を確立しないため accept は不要
        //  1 つの通信をすべての処理に使いまわす
        // 
        printf( "recvfrom にはいります\n" );
        int n = recvfrom( sock, buf, sizeof(buf), 0, (struct sockaddr *)&adr, &len );
        buf[ n ] = '\0';
        printf( "client msg %s\n", buf );

        sendto( sock, buf, strlen(buf), 0, (struct sockaddr *)&adr, len );

      }
    }


___

■ recvfrom

SYNTAX int recvfrom( SOCKET sock, void *msg, unsigned int length, int flags struct sockaddr *adr, unsigned int & adrlength ) RET N : 受信したメッセージのバイト数 -1 : エラー DESC UDP ソケットでメッセージを受信する TCP の recv() と同じようにブロックされる。 最後に2つの引数でデータグラムの送り主の情報がわかる。 POINT TCP のように接続処理をしないため 特定の相手以外からのデータも受信される。
    // ip の値を比較して判断する。

    if ( dst.sin_addr.s_addr  != src.sin_addr.s_addr ) {
      printf( "unknown server" );
    }
___

■ broadcast

DESC broadcast とは特定のネットワークに向けて無差別に送信する方法のこと。 multicast は条件にあった相手にだけ送信される。 IP では broadcast, multicast が利用できるのは UDP だけ。 POINT broadcast, multicast の使い分けはメッセージを要求するホストの割合や, 通信相手の情報の有無で考える。 インターネット規模でのブロードキャストは規模が大きすぎるため ブロードキャスト用のパケットを転送しないルータが多い。 broadcast は LAN 内で使用することが一般的。 逆にホストを見つけるには broadcast が適しているため プリンタはどこか? といった質問は向いている。

    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      // UDP 用のソケット作成
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);


      // 送信先をネットワーク内すべてに指定する
      struct sockaddr_in adr;
      adr.sin_family = AF_INET;
      adr.sin_port = htons(7777);
      adr.sin_addr.S_un.S_addr = inet_addr( "169.254.255.255" );


      // ブロードキャスト用のソケットに変更
      int param = 1;
      if ( setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *)¶m,  sizeof(param) ) <  0 ){
        printf( "fail setsockopt\n" );
        return;
      } 

      // POINT
      //  3 秒ごとに指定した特定のネットワークにブロードキャストをする
      //    
      while(1) {

        printf( "broadcast message 送信\n" );

        char buf[] = "test broadcast";
        int len = sizeof(adr);

        // 他のソケットが使用していないポート番号が自動で設定される。
        // サーバはこのポートを見て返事を返すので特に知る必要はない
        sendto( sock, buf, strlen(buf), 0, (struct sockaddr *)&adr, len );

        Sleep(1000 * 3);
      }

    }




ブロードキャストを待ち続けるレシーバ側の処理
    // サーバーソケットを作成する
    SOCKET createSocket( unsigned short port ) 
    {
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

      struct sockaddr_in adr;

      adr.sin_family = AF_INET;
      adr.sin_port = htons(port);
      adr.sin_addr.S_un.S_addr = INADDR_ANY;  // サーバ側で割り当てられている IP を自動で設定

      bind(sock, (struct sockaddr *)&adr, sizeof(adr));

      return sock;
    }


    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      SOCKET sock = createSocket( 7777 );

      int cnt = 0;
      while(1) {

        struct sockaddr_in adr;
        int len = sizeof(adr);

        // 移行のクライアントとの対話を普通にすると当然 recv などでブロックされる。
        char buf[256];
        printf( "recvfrom にはいります\n" );
        int n = recvfrom( sock, buf, sizeof(buf), 0, (struct sockaddr *)&adr, &len );
        buf[ n ] = '\0';
        printf( "broadcast msg %s\n", buf );

      }
    }
___

■ multicast

POINT マルチキャストは特定のグループのメンバーにだけ送信する。 この集合をマルチキャストグループといい、 受信をしたい人はこのグループに登録する必要がある。 IP にはマルチキャスト用に用意されたアドレスの範囲があるため その中から任意のものを宛先に指定する。
    224.0.0.0 - 239.255.255.255

    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      // UDP 用のソケット作成
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

      // マルチキャストグループを宛先に指定する
      struct sockaddr_in adr;
      adr.sin_family = AF_INET;
      adr.sin_port = htons(7777);
      adr.sin_addr.S_un.S_addr = inet_addr( "224.1.2.3" );


      int param = 1;
      if ( setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)¶m,  sizeof(param) ) <  0 ){
        printf( "setsockopt fail\n" );
      }
      else {
        printf( "setsockopt success\n" );
      }

      // POINT
      //  3 秒ごとに指定した特定のネットワークにマルチキャストする
      while(1) {

        printf( "multicast message 送信\n" );

        char buf[] = "test multicast";
        int len = sizeof(adr);

        // 他のソケットが使用していないポート番号が自動で設定される。
        // サーバはこのポートを見て返事を返すので特に知る必要はない
        sendto( sock, buf, strlen(buf), 0, (struct sockaddr *)&adr, len );

        Sleep(1000 * 3);
      }

    }
マルチキャストレシーバの処理 マルチキャストグループに登録して受信する。
  // WARNING 
  //  setsockopt( IP_ADD_MEMBERSHIP ) で失敗するため 
  //  winsock.h ではなく winsock2.h をインクルードすること

    #include < winsock2.h>
    #include < ws2tcpip.h>
    #pragma comment(lib, "ws2_32.lib")

    // サーバーソケットを作成する
    SOCKET createServerSocket( unsigned short port ) 
    {
      SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);

      struct sockaddr_in adr;

      adr.sin_family = AF_INET;
      adr.sin_port = htons(port);
      adr.sin_addr.S_un.S_addr = INADDR_ANY;  // サーバ側で割り当てられている IP を自動で設定

      bind(sock, (struct sockaddr *)&adr, sizeof(adr));

      return sock;
    }


    void main()
    {
      WSADATA wsaData;
      WSAStartup(MAKEWORD(2,0), &wsaData);

      SOCKET sock = createServerSocket( 7777 );


      // マルチキャストグループへ登録( join )する
      struct ip_mreq mr;
      mr.imr_multiaddr.s_addr = inet_addr( "224.1.2.3" );
      mr.imr_interface.s_addr = INADDR_ANY;
      if ( setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mr,  sizeof(mr) ) <  0 ){
        printf( "setsockopt fail\n" );
      }
      else {
        printf( "setsockopt success\n" );
      }

      while(1) {
        struct sockaddr_in adr;
        int len = sizeof(adr);

        // 移行のクライアントとの対話を普通にすると当然 recv などでブロックされる。
        char buf[256];
        printf( "recvfrom にはいります\n" );
        int n = recvfrom( sock, buf, sizeof(buf), 0, (struct sockaddr *)&adr, &len );
        buf[ n ] = '\0';
        printf( "multicast msg %s\n", buf );
      }
    }



___

■ setsockopt

SYNTAX int setsockopt( SOCKET sock, int level, // 指定したいオプションのプロトコル int name, // 設定したいオプション const char *val, // オプションの値へのポインタ unsigned length, // オプションのバイトサイズ ) DESC ソケットのオプションの値を変更する。 RET 0 : 成功 -1 : 失敗 POINT 設定できる項目はプロトコルスタックのレイヤーごとに異なる
    SOL_SOCKET
    IPPROTO_TCP     // TCP プロトコルへの制御オプション
    IPPROTO_IP
    // recv() のタイムアウト時間を 3000 msec にする
    unsigned int t = 3 * 1000;
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&t, sizeof(t) );


    // タイムアウトを1秒に設定
    unsigned int t = 1 * 1000;
    getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&t, sizeof(t) );
    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&t, sizeof(t) );
___

■ getsockopt

SYNTAX int getsockopt( SOCKET sock, int level, int name, // 設定したいオプション char *val, // オプションの値をもらうポインタ unsigned *length, // オプションの値のバイトサイズ ) DESC ソケットのオプションの値を取得する。 RET 0 : 成功 -1 : 失敗
    recv() の受信バッファサイズを2倍にする

    int sz;
    int len = sizeof( sz );
    getsockopt( sock, SOL_SOCKET, SO_RCVBUF, &sz, &len );
    sz *= 2;
    setsockopt( sock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz) );








金利比較.COM
NINJAIDX 13