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

■ 使い方


___

■ 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) );

___

■ リファレンス(Reference)






___

■ 共通

___

■ socket

SYNTAX SOCKET socket ( int af, // ソケットタイプ( アドレスの種類を表すアドレスファミリ ) int type , // ソケットの種類( SOCK_STREAM ) int protocol // (TCP | UDP) ); RET 0: OK SOCKET_ERROR: FAIL DESC 指定したタイプの Socket を作成して SOCKET HANDLE( 整数値 ) を返す。

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

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

■ bind

SYNTAX int bind ( SOCKET s , const struct sockaddr FAR* name , int namelen ); RET 0: 成功 SOCKET_ERROR: 失敗 DESC ソケットにローカルIPアドレスとローカルポートを設定する。 Server 側は指定した port で listen するため bind して明示する Client 側は自動設定にまかせる。 ERROR EADDRINUSE : ポートが他のソケットで利用されている。
    {
      SOCKADDR_IN adr = { AF_INET };
      adr.sin_port = htons(80);
      adr.sin_addr = INADDR_ANY;       // ローカルアドレスにワイルドカードを指定
                                       // サーバー側のすべてのIPアドレスでデータを受け取ることができる
      
      SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
      bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
    }
WARNING
    // 同じローカルポートに他のソケットがバインドしている場合はエラーになる

    SOCKET sock1 = socket(AF_INET , SOCK_STREAM , 0);
    bind( sock1 , (SOCKADDR *)&adr , sizeof(SOCKADDR) );

    SOCKET sock2 = socket(AF_INET , SOCK_STREAM , 0);
    bind( sock2 , (SOCKADDR *)&adr , sizeof(SOCKADDR) );
___

■ SOCKADDR_IN

インターネットアドレス形式を表す構造体 union で定義されているが, ほとんどは S_addr に IP Address を表す 32bit の値をいれる
    typedef struct sockaddr_in {

      short            sin_family;
      u_short          sin_port;        // ポート
      struct in_addr   sin_addr;        // 
      char             sin_zero[8];     // 

    } SOCKADDR_IN;
POINT sin_port にはサーバがサービスを提供するポート番号を指定する sin_addr には INADDR_ANY を指定することで固定アドレスでない場合に自動で割り振られる
___

■ send

SYNTAX int send( SOCKET sock, char *msg, // メッセージのポインタ int length, // メッセージの長さ ( byte 数 ) int flags // 通常 0 ( | MSG_DONTROUTE,MSG_OOB ) ); RET N : 実際に送信されたバイト数 SOCKET_ERROR : エラー DESC 指定したソケット経由でデータを送信する(主に TCP で使用) flags で 0 を指定するとデフォルトの振る舞いをする。 正確にはソケットの送信バッファへデータがコピーされてすぐに制御を返す。 STATE ソケットは接続状態( ESTABLISHED )である必要がある。 // HTTP Client が HTTP Server に HTTP Message ( GET / )
    {
      SOCKADDR_IN adr = { AF_INET };
      name.sin_port = htons(80);
      
      SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
      bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );

      const char *msg = "test";

      // WARNING
      //  メッセージは長さを送り, 末尾の '\0' は不要
      send( sock, msg, strlen(msg), 0 ) ;
    }
___

■ recv

SYNTAX int recv( SOCKET sock, char* buf, int length, int flags // 通常 0 ( MSG_PEEK MSG_OOB ) ); RET N : 読み込まれたバイト数 SOCKET_ERROR : 失敗 0 : 通信相手がコネクションを閉じた DESC 指定したソケットで相手からデータを受信をする。( 主にTCPで使用 ) 正確にはソケットの受信バッファにあるデータを buf で指定した領域へコピーする。 受信バッファにデータがなければブロックされるため メインスレッドから実行する場合は何らかの対策が必要。 STATE ESTABLISHED 接続状態でない時に利用すると SOCKET_ERROR が返る。 WARNING HTTP Server が相手だとリクエストを投げない( send() をしない )と何もかえってこない このとき recv() で Block される。 ブロックを回避するには, recv() 用のスレッドを作成したり、非同期ソケットを利用する。

    // シンプルに受信する
    char buf[1024];
    int len = recv(sock, buf, sizeof(buf), 0);
    buf[len] = '\0';

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

    if ( len == SOCKET_ERROR ) return 1;


WARNING 十分なバッファを WinSock に渡しても ソケット上のキューにあるデータを全て返してくれない 全てとるには呼出しを繰り返す
    char buf[1024 * 4];
    memset(buf, 0, sizeof(buf));

    int sum = 0;
    while ( 1 ) {

      int len = recv(sock, buf + sum, sizeof(buf) - sum, 0);
      sum += len;

      // 0 がきたら相手が CLOSE したことを意味する。
      if ( len == 0 ) {
        printf( "server closed\n" );
        break;
      }

      // 相手が close した状態で recv() をしたり, サーバが落ちていると -1 が返る。
      if ( len == SOCKET_ERROR ) {
        break;
      }

    }
FD_CLOSE がうけれるなら, len == 0 の処理は不要 逆に FD_CLOSE Event がくるときに , recv をすると, len = 0 がかえってくる 0 == recv() は FD_CLOSE をチェックしているに等しい

    // Thread にわたす 受信処理
    void* waitReceiveThread(void* pParam)
    {
        char buf[1024];
        int len;

        while(1)
        {
           // データ受信待ち
           len = recv(sock,buf,1024,0);

          printf("Received %d bytes\n",len);

          // 相手が閉じたら 
          if(len == 0)
          {
              printf("check\n");

              // こっちも切る
              close(sock);
              break;
          }
        }

        return NULL;
    }
connect が失敗した状態のソケットで recv( 受信 ) をすると WSAENOTCONN (10057) が返る。 接続されていないソケットを使用したということ。 同じく send() でも発生する。
    // no check
    connect( sock, (struct sockaddr *)&adr, sizeof(adr) );

    int n = recv(m_sock, buf, sz, 0);
    if (n <  0) {
      printf("ERR: recv : %d\n", WSAGetLastError() );
    }  
___

■ closesocket

SYNTAX int closesocket ( SOCKET sock ); RET OK : 0 FAIL: SOCKET_ERROR DESC コネクションを切る。 ソケットは通信できない状態へ移行する 使うと Client 側には HOST との接続が切られました という message が届く
___

■ server.固有

___

■ listen

SYNTAX int listen ( SOCKET s , // 受信に使う Socket int queue // 接続に使うキューの数 ( 最大接続数 ) ); RET 0: 成功 SOCKET_ERROR: 失敗 DESC サーバのソケットが接続待ち状態( LISTENING )に移行して、 クライアントからの接続要求がキューへ格納できる状態へする。 queue で指定した数だけ最大でリクエストを保存できて、 アクセプト( accept )をするとキューから取り出す。 queue 以上のクライアントからの接続要求は失敗する。 POINT サーバが listen() しないとクライアント側のコネクト要求( connect() )は失敗する。
    {
      SOCKADDR_IN adr = { AF_INET };
      adr.sin_port = htons(2048);
      adr.sin_addr = INADDR_ANY;
      
      SOCKET sock = socket(AF_INET , SOCK_STREAM , 0);
      bind( sock , (SOCKADDR *)&adr , sizeof(SOCKADDR) );

      // キューの数を5にしてリスン状態にする。
      listen( sock, 5 );
    }
___

■ accept

SYNTAX SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, // クライアント側のアドレス情報 int FAR* addrlen ); RET ClientSocket : 成功 INVALID_SOCKET : 失敗 DESC クライアントからの接続を受け付ける。 正確には リスン中のソケットの接続すみリストからひとつを取り出してディスクリプタを割り当てて返す。 リストが空ならばブロックされため、 GUI の MainThread では利用しないこと。
    {

      SOCKET sockCli = accept( sock,   );


      // お客さんへメッセージをなげる。
      const char buf[] = "いらっしゃい";
      send( sockCli, buf, length(buf), 0 );

    }
___

■ client.固有

___

■ connect

SYNTAX int connect( SOCKET, struct sockaddr *adr, int size ); RET SOCKET_ERROR : 接続に失敗 0 : 成功 DESC 指定したサーバに接続する。 不正なアドレスを指定すると失敗する。 ERROR ETIMEOUT : 指定した時間にハンドシェイクが完了しなかった。 WARNING Server でリスン( listen() )してない場合はすべての接続要求は失敗する。 逆に listen() していれば accept() をしなくても接続要求は成功する。 ただしリストが埋まっていれば失敗する。
    struct sockaddr_in adr = { AF_INET };
    adr.sin_port = htons( 80 );
    adr.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" );  

    int ret = connect( sock, (struct sockaddr *)&adr, sizeof(adr) );

    if ( ret == SOCKET_ERROR ) {
      printf( "fail connect\n" );
    }
host が存在して ping などで接続できるが, 一定時間がたっても応答がない場合は 20秒ほど経過してから 10060 ( WSAETIMEDOUT ) が返る。 ケースとしてクライアントが指定したポートでリスンしてないなどがある
    adr.sin_port = htons( 8888 );
    int ret = connect( sock, (struct sockaddr *)&adr, sizeof(adr) );

    if ( ret != 0 ) {
      printf("FAIL connect : WSAGetLastError  %d\n", WSAGetLastError());       
    }
___

■ 変換

___

■ htons

SYNTAX u_short htons( u_short ); DESC short int をネットワークバイトオーダーに変換 SYNTAX u_short htons ( u_short hostshort // 変換する 16 ビットホストバイトオーダ ); ); RET 指定した値のネットワークバイトオーダー DESC 16 ビットホストバイトオーダーをネットワークバイトオーダーに変換する
    {  
       struct sockaddr_in srcAddr;
              
       // short 型の Data のときに使う
       srcAddr.sin_port = htons( port );
    }
___

■ htonl

SYNTAX u_short htonl ( u_long hostshort // 変換する 32 bit HostByteOrder ); RET 指定した値の NetworkByteOrder DESC htons の long (32bit) Version
    {  
       struct sockaddr_in srcAddr;
       srcAddr.sin_addr.s_addr  = htonl( INADDR_ANY );
    }
___

■ ntohs

SYNTAX u_short ntohs( u_short netshort ); DESC short の整数を ホストバイトオーダー に変換する Client の port 情報を変換する際に便利
___

■ getHostByName

SYNTAX struct hostent * getHostByName( char *name ); DESC ホスト名( 又は dot表記のIPアドレス )から 32bit IPAddress をえる 逆に IP からホスト名とりたい場合は getHostByAddr() を使う DNS にサーバー名とアドレスを登録しておく必要がある。 RET !NULL : 変換されたホスト情報へのポインタ NULL : 不正なホスト名( dot表記IPアドレス )を指定した。 POINT IPアドレスでもあて先を指定できるが、ISPの変更があった場合に影響を受けないように ホスト名を指定しておくと良い。
     LPHOSTENT host = getHostByName( "google.co.jp" );

  POINT
    // ホスト情報を格納した構造体 hostent を返す。

    typedef struct hostent {
       char *h_name;              // ホスト名
       char **h_aliases;          // ホストの別名


       char **h_addr_list;        // 32bit IPアドレスのポインタ。プログラムはこの値を指定すること。
    };
    

___

■ getHostByAddr

SYNTAX struct hostent * getHostByAddr( char *addr, int len, int type ); DESC IP Address から Host情報をえる
___

■ inet_addr

SYNTAX int inet_addr( const char FAR * // 文字列表現アドレスへのポインタ ( "xxx.xxx.x.x" ) ); RET アドレスの数値表現: 成功 INADDR_NONE : 失敗 DESC ドット表記のIPv4アドレス文字列を 4byte の整数(ネットワークバイトオーダー)へ変換
    BAD
      // ドメイン名はダメ
      inet_addr( "library.city.chuo.tokyo.jp" )
    OK
      // dot 表記を渡すこと
      inet_addr( "210.233.111.187" );
___

■ inet_ntoa

SYNTAX char * inet_ntoa ( struct in_addr in // アドレスが格納されている in_addr 構造体 ); RET 文字列へのポインタ : 成功 NULL : 失敗 DESC バイナリ表現の IPアドレスを 10進数のドット表記文字列( "127.0.0.1" など )に変換。
___

■ WinSock.固有

___

■ WSAStartup

SYNTAX int WSAStartup ( WORD wVersionRequested, // 使用したい version LPWSADATA lpWSAData); // WinSock の実装情報を取得する. RET 0: OK !0: FAIL DESC DLL を初期化する
___

■ WSACleanup

SYNTAX int WSACleanup (); RET 0: OK !0: FAIL DESC WinSock の Resource を解放する
___

■ WSAGetLastError

SYNTAX int WSAGetLastError() DESC 最後( 直近 )に発生したエラーコードを返す。
___

■ 非同期.API

___

■ WSAWaitForMultipleEvents

SYNTAX DWORD WSAWaitForMultipleEvents( 1, // Event数 &hEvent, // EventObject FALSE, 5*1000, // 待ち時間 msec FALSE ); DESC Socket に関連つけられたネットワークイベントを指定時間まつ (connect | recv | cloes )をまつ

    // Accept を指定する

    // 
    WSAWaitForMultipleEvents()

    // Client が接続してきた !
    if ( FD_ACCEPT ) {

    } 


___

■ WSAEnumNetworkEvents

SYNTAX nRet = WSAEnumNetworkEvents( Socket, hEvent, &events ); DESC 発生したNetworkイベントの種類を調べる
___

■ WSAEventSelect

SYNTAX nRet = WSAEventSelect( Socket, // 関連づける Socket hEvent, // Event FD_READ|FD_CONNECT|FD_CLOSE // 通知をうける Event の種類を設定 ); RET 0 : 成功 !0 : ERRORCODE DESC ソケットを[ 非ブロッキング ]にし[ ネットワークイベント ]を関連付ける イベントがおきると イベントオブジェクトが Signal 状態になる yfWaitAll [in] A value that specifies the wait type wait の type を指定する If TRUE the function returns when the state of all objects in the lphEvents array is signaled If FALSE the function returns when any of the event objects is signaled ( すべての Event に対してかえす ) In the latter case the return value minus WSA_WAIT_EVENT_0 indicates the index of the event object whose state caused the function to return If more than one event object became signaled during the call this is the array index to the signaled event object with the smallest index value of all the signaled event objects なんか false でよさげ fAlertable [in] 〈物・事が〉変えられる, 改められる. A value that specifies whether the thread is placed in an alertable wait state so the system can execute I/O completion routines If TRUE the thread is placed in an altertable wait state and WSAWaitForMultipleEvents can return when the system executes an I/O completion routine In this case WSA_WAIT_IO_COMPLETION is returned and the event that was being waited on is not signaled yet The application must call the WSAWaitForMultipleEvents function again If FALSE the thread is not placed in an altertable wait state and I/O completion routines are not executed
___

■ データ型

___

■ sockaddr

DESC 汎用のアドレス構造体 特定のアドレスファミリ固有ではない。
    struct sockaddr {
      unsigned short sa_family;
      char sa_data[14]
    };
___

■ sockaddr_in

DESC インターネットアドレスファミリの構造体 POINT sockaddr 構造体をインターネットファミリ向けに定義しなおしただけのため、 sockaddr を受け取る関数にキャストして渡すことができる。 設定する値はすべてネットワークバイトオーダーであること。
    struct sockaddr {
      unsigned short sin_family;
      unsigned short sin_port;      // ポート 
      struct in_addr sin_addr;      // ip

      char sin_zero[8];             // 未使用領域
    };
___

■ ReferenceCommand


  Network Program に便利なコマンド


___

■ nslookup

SYNTAX nslookup domanin DESC DomainName から .表記 IP を返す
    // この値を inet_addr() に渡して接続先を指定する
    nslookup google.co.jp
___

■ tracert

SYNTAX tracert domain


___

■ netstat

POINT 接続状況とソケットの状態を確認できる。 ローカルでテストをする場合はサーバで設定したポートは ローカルアドレス側にくる。 クライアント側は外部アドレスに表示される。
    プロトコル   ローカルアドレス     外部アドレス           状態
    TCP          xxx.xxx.xxx.xx:yy    xxx.xxx.xxx.xx.yyy     ESTABLISHED
___

■ URLEncode

  
  DESC
    URI( Address )に含むことができない文字コードを使用できる文字に変換する
    RFC では Percent-Encoding という名称


    文字列データをURLに埋め込むようなときは
    Webサーバとの間で正しく情報をやりとりするために
    別の表記形式に変換する(エンコードする)必要がある

    URI の規則を定める RFC 3986 では URI に
    ASCII の非予約文字以外の文字データを用いる場合には
    「%xx」( xxは16進数 )という形でコードを表記することが定められている

    こんな問題がある
     文字列データに空白文字, 一部の記号(「$」「#」など)
     全角文字などを含んでいると正しく送信されないことがある
問題は. 日本語( 2 byte 文字 ) ServletEngine( Tomcat )で正しく変換されないこと Client からの 日本語をうけつける際に, 文字化けをおこすこと 要は, Gateway が 解釈できるように, 1 byte 文字に変化してしまう データをURLに埋め込んでも問題が生じないように 英数字以外の文字や/などの記号を安全な形に変換することが目的   ( さらに SPC, 制御文字も可視化する ) その際にどの文字コードを用いるかは実装によって異なる Rule からはみ出る文字は, URLEncode( 16 進 ASCII 表記 ) される SPACE < -> Opera( Yahoo::jisyo ) で試してみた. [+] ---> [%2B] # ASCII ですね. [ ] ---> [+] [&] ---> [%26] [?] ---> [%3F] # ASCII ですね. 変換方法 1. Byte 単位でされる. 1. 第1 Byte -> 16 進. 1. 第2 Byte -> ASCII の文字範囲に収まる場合は, そのままの文字. 1. 各項目は [&] を接続文字になる http://house.o-uccino.jp/detail_0006131464_h/ ただし、URL エンコードの変換方法は JavaScript と CGI では一部異なる スペースは JavaScript の場合には %20 に変換されるが、 CGIでは + に変換される URLEncoding が必要な理由 メール( SMTP )や HTTP などのパケット( Dataの塊 )は ヘッダ部に宛先, その他メッセージの制御に関わる情報が追記されてます このヘッダは途中のゲートウエーを幾つか中継され、端から端のノードに伝達されます これらのノードに理解できるコードと文字で表現されなければならない ヘッダ部に日本語のような 2 バイト文字が入ると、 ノードはこれを 1 バイトずつ解釈して誤解してしまう これを避けるため URL エンコードができた 名前と値にある「安全でない」文字は"%xx"に変換する "xx"はその文字のASCII値を16進表示したもの しかし送られる情報をすべて「見える」文字列に変換するのは都合が良いことが多く ボディ部分にも使われる ボディ部分の変換にはもうひとつ MIME(Multi-Purpose Internet Mail Extensions)エンコーディングがある これは2バイトのバイナリ・データを3バイトの 7ビットASCII(American Standard Code for Information Interchange) 文字に変換するもので マルチメディア情報の転送に使われる RFC 2396 2.4 Browser で実行される URLEncode は htm file の文字コードでされるみたいです Encode する文字
     空白, 記号 ( $, #, &, >, <  スペース )
     MultiByte 文字
     Print できない文字
     これ未確認です [&?]
  
  HTTP message に [&], [?] の Escape は不要
変換するのは [ = & % + ] プリントできない文字 [&] -> [%26] [%] -> [%25] [+] -> [%2B] [=] -> [%3D] [< ] -> [%3C] [>] -> [%3E] 特別なルールとして ASCII スペース[ ] -> [+] Encode しない文字
   [A:z]         // Alphabet
   [0:9]         // 数字
   ['.-*)(_]     // ↑以外の 記号
IE をつかって google で検索をすると URLEncode の結果がみれます

".NET TIPS"

を検索したときには
検索結果の画面でのURL(IEの[アドレス]部分)が次のようになっているはずだ



 このURLでは
  検索文字列  ".NET TIPS" が
  「%22.NET+TIPS%22」にエンコードされている(「"」が「%22」に
  半角スペースが「+」に変換される


  POINT
    IE が自動的にエンコードをしてるが
    プログラムで同様のリクエストを送信するときは
    文字列データを自前でエンコードすること

URL に Encode が必要な文字を含めるには URLEncode 変換後の文字をいれる
    "B&B" を URL に含めたい

    "&" は変換する文字 -> "%26"
    "B%26B"

変換するには, 単に 1byte ずつ %[16進数]といいかえるだけ
    byte をなめるので 文字コードによって表記はかわる

    POINt
        % とついていないのは そのまま ということ

    // ASCII Code は Encode しなくてもいいです
    Lucky池田 < -> Lucky%92r%93c
// SJIS [あ] を UrlEncode あああ < -> %82%A0%82%A0%82%A0 // [ニュース速報] を UrlEncode EUC-JP %A5%CB%A5%E5%A1%BC%A5%B9%C2%AE%CA%F3 SJIS %83j%83%85%81%5B%83X%91%AC%95%F1 JIS %1B%24B%25K%25e%21%3C%259B.Js%1B%28B UTF-8 %E3%83%8B%E3%83%A5%E3%83%BC%E3%82%B9%E9%80%9F%E5%A0%B1
逆に 何の Encode の URLEncode かを指定しないと元に戻せない # [ あああ ] を 辞書ページで検索してみた. ( これこそまさに DB の機能が使えそう. ) # p = =%E3%81%82%E3%81%82%E3%81%82 # あ = %E3%81%82 となる. # あ = E38182 ( これ何の文字コード ? ) elisp/urlencode/src/urlencode.el * TextEditor で 文字をかいて , 特定の文字 Code で保存して dump して % つけたもの TIP emacs では urlencode-region なんてものを 利用すると便利 すべての WebAddress を UTF-8 で Encode する たいていの WebApplication 用の言語には URLEncode のメソッドがある

  // C# VB.NET  HttpUtilityクラス(System.Web名前空間
  HttpUtility.UrlEncode( "あいうえお" )

  // Java ( JSP )
  
この文字列が POST のボディ部分 あるいは GET のクエリとしてはめ込まれるのである Tomcat は URL をデコードして Java の使用文字コードの Unicode に変換します
      [ URL からのデータ ]
            |            Decode 前の文字Code は ISO8859_1 とみなして StringObject に変換
                        ( Tomcat が URL を Decode して Java の内部 Code の Unicode に変換 ) 
            |
            |
      [ StringObject ] 
            |            
            |        < - ISO8859_1 の byte 列にもどす
            |        name.getBytes( "8859_1" );
                    URLDecode 直後の状態になる
            |
      [ Byte 列 ]
            |            
            |        Byte 列 を SJIS として, 再度 StringObject をつくる
            |        String( byteArray, "Shift_JIS" )
            |
      
まとめ Tomcat で日本語を使った JSP を利用するためには, 次の方法をする

  String wordSjis = new String( word.getBytes("8859_1"), "Shift_JIS" );

1. getBytes(“8859_1”) で一旦 Unicode の文字列を ISO8859_1(Latin-1) に変換したうえでバイト列へ URLデコードしたときのバイト列が得られることになる そのバイト列をString(bytes,”Shift_JIS”)のコンストラクタで Shift_JIS のバイト列だと指定して String型のオブジェクト(Unicode)をつくる "Shift_JIS" の部分は "EUC-JP" , "JISAutoDetect" もともと期待していたエンコーディングを指定する "JISAutoDetect" は短い文字列の場合に正しく判定しないことがあるので使用しないほうが好ましい まずしたうえでバイト列にする そうするとURL デコードした直後のバイト列がとれます そのうえでそのバイト列をString(bytes,”Shift_JIS”)のコンストラクタでShift_JISの バイト列だと指定して String 型のオブジェクト(Unicode)を生成する 名前と値を=と&でつないでひとつの文字列にする EX: name1=value1&name2=value2&name3=value3 htm の form で送っているやつか. POINT // 実は [?] 以降は HTTP のヘッダを渡していた. http://server/foo.jsp?name=val&name=val&name=val&
___

■ MIME

DESC MultiPurpose Internet Mail Expression ( 多目的 internet ) 規格のひとつ. ( Internet で Data のやりとりをする時の規格 ) internet の 文字 Code は 7bit だから binary を扱うときは uuencode で変更して uudecode で戻していた でも internet が普及したので Programmer でない人がこれをするのは不便 そこで MIME という規格をつくり, Binary を扱えるようにした ボディ部分の変換にはもうひとつ MIME(Multi-Purpose Internet Mail Extensions) エンコーディングがある( これも Encode ) 1. MultiPurpose Internet Mail Expression ( 多目的 internet ) これは2バイトのバイナリ・データを3バイトの7ビットASCII(American Standard Code for Information Interchange)文字に変換するもので マルチメディア情報の転送に使われる ( binary から ASCII に置換するのが目的 )
___

■ 基本的なこと

___

■ Socket

DESC Socket とは ユーザにとってのデータの出入り口 ユーザはソケットへデータを読み書きするだけで通信できる。 ( とりあえず FILE Pointer みたいなものだと思えば OK ) Socket の裏側の通信 Protocol を意識しなくても通信Programができます Socket は 2 つ以上の Soket が関係をもって初めて意味があります ( 1 : N であることもある ) Socketの種類 DESC 2 つあります
  停止型(ブロック) 
  非停止型(ノンブロック)
Block とは パケットがくるまでスレッドがブロックされるモードのこと パケット到着まで他の作業はできません 受信では アプリケーションが WSARecv 関数か WSARecvFrom ()に受信用のメモリを提供する 受信前に一つ以上のメモリを用意しておくと データが到着したらそのメモリにすぐ配置される それらの関数を使えば recv 関数か recvfrom 関数の呼びだし時にコピーしなくて済む 受信用メモリを提供したときに すでにデータがあれば すぐにそのメモリにコピーされる アプリケーションが受信用メモリを用意していないときにデータが到着すると ネットワークがお決まりの形式で同期的に処理します 内部のメモリに保存します アプリケーションが setsockopt 関数で受信用の一時メモリの大きさをゼロに設定した場合は例外です 信頼性が保証されている通信規格では アプリケーションがメモリを用意するとその分のデータを受信できる 信頼性が保証されていない通信規格ではデータを失う
___

■ Packet

DESC バイト列のことをネットワーク上ではパケットという。 送信されるデータだけではなく、宛先などの制御情報が含まれる。 この情報をみてルータなどは制御をする。 小包での宛先と中身みたいなもの。
      [ ヘッダ ][   データ   ]
___

■ Protocol

DESC 通信プログラムが情報を交換する際の手順や, パケットの内容についての決まりごと ネットワークで通信しあうための各ルールを階層構造のモジュール単位でわけている。 これを プロトコルスイート, プロトコルファミリという ( TCP/IP など )
        [ ソケット ]
          [ HTTP ]
      [ TCP ][ UDP ]
          [ IP ]
___

■ ApplicationProtocol

DESC TCP/IP プロトコルがしてくれるのは データを送信したり、受信をすることだけ。 実際のソケットを使ったプログラムでは やり取りする情報をどんな形式で、いつどちらから送るかなどの取り決めが必要になる。 これをアプリケーションプロトコルという。
___

■ Order

POINT ソケット経由で多バイトのデータを送信する場合は アドレスの昇順で送る。 受信側も同じく昇順で受信する必要がある。 昇順とは data の小さい順番に並べること。階段でいうと上にあがるのと同じ 送信側と受信側のプロセッサ固有のバイトオーダーが逆になると問題がおきる POINT あるマシンから別マシンへ多バイトのバイナリ値を送る場合は hton*, ntoh * を使うこと。 もし ネットワークのバイトオーダーとプロセッサ固有のオーダーが同じ場合は 変換処理はされない。
___

■ バッファリング

POINT
    recv(), send() は一対一対応をしていない。
TCPコネクション上を流れるデータは全体でひとつのデータ列とみなせるが 3箇所のバッファに分かれて存在する。
      send() -> [ SendQ ] ---> [ RecvQ ]  --(recv())--> [ Deliverd ] 
send() は SendQ にすべてのデータが入るまでブロックされる。 複数の send() が1回の recv() で受け取ることがあるため 受信するときは メッセージの解析をすることが必須。 通常は 区切り文字でする。
___

■ ソケットの実装

POINT TCP ソケットの理解するには ソケット構造体やプロトコルの仕組みを理解することが大切 ソケット構造体は次の情報をもつ
    [ローカルIPアドレス]
    [ローカルポート]
    [リモートIPアドレス]   // ローカルソケットの接続先を指定する
    [リモートポート]
    [受信バッファ]
    [送信バッファ]
POINT ローカルポートは bind() で明示できる。また初めて使用する際に自動でアサインされる。 サーバは bind() をコールして明示して、クライアントはお任せするのが普通。
___

■ ソケットの状態

[ closed ] ---> [ ] POINT
    send(), recv() に対応関係があるとは限らない

    1回のsend() を複数の recv() で受けとったり
    3回のsend() を1回の  recv() で受けとることもある
    
POINT
    TCP ソケットで send(), recv() を呼び出せるのは 状態が Establishing のときだけ
connect() bind() を指定していないので 他のソケットが利用していないポートが自動でアサインされる [ Closed ] -> [ Connecting ] -> [ Establishing ] [ ] [ P ] [ ] [ P ] [ ] [ P ] [ ] サーバ側 bind() listen [ Closed ] -> [ Closed ] -> [ listening ] [ ] [ Q ] [ ] [ ] [ ] [ ] [ ] connect があった場合に新規ソケットを作成して IP, リモートポートが割り当てられる 接続可能なソケットリストに入る accept() は Establishing なソケットをひとつ返す -> [ Establishing ] [ ] [ ]
___

■ 切断処理の流れ

POINT TCP には正当な切断処理という仕組みがある これにより アプリケーションは転送中のデータを損失せずに済む。 POINT 切断は双方向に行うことができる。 close(), shutdown() は データ送信の終了を表明する。 close() を受けた TCP 側は 送信バッファの情報の残りをすべて送信して 切断の意思(ハンドシェイク)を送信する。 これで受け手はこれ以上受信バッファにデータがこないことを確認できる。 これが recv = 0 ( close() )の意味 肯定応答があれば送信した側の情報がすべて 受信バッファに格納されたことが確認できる。 コネクションは Half-Colsed の状態になる これを双方に行うことで完全に切断する。 POINT ディスクリプタの割り当てが解除されても ソケット自体は TCP に残る。 これが Time-Wait の状態。 Time-Wait がある意味は ソケット構造体が存在し続けて 他のプログラムが同じポートのソケットをバインドすることを防ぐことにある。 ( EADDINUSE を返す ) POINT 後から接続を解除する場合 相手からの解除要求がきて肯定応答をすると Close-Wait 状態に移行する [ Establishing ] ---> [ Close-Wait ] このとき Application の close() 待ちになる。 close() を呼ぶと ソケット構造体へのディスクリプタが解除される。 そして肯定応答が返ると ソケット自体が消滅する。 ( つまり 互いにすべての情報を相手側に送れたことを確認できるため )
___

■ Firewall






  DESC
     プログラムのインターネットへの接続の可否と種類を制御する
     ファイアウォールが搭載されてる。

     Windows ファイアウォールが通信を遮断していると
     Web サイトにアクセスしたときに「サーバが見つかりません」とエラーが表示される


    インターネットなどからの不正なアクセスを防ぐことのできるセキュリティ機能
    外部からの侵入を防ぐ System(Software)

    NetworkProgram をつくったときは, 自分で 用意している port をあけないとだめ


  POINT
    これがないと, 危険な Client があると , 好き勝手されてしまう

    // こんな感じに穴をあける
    //
    [ 不正な Program ] [ port ] < ---[FireWall]< ----[  外部  ]
    [ 不正な Program ] [ port ] < ---[FireWall]< ----[  外部  ]
    [ 自分の Program ] [ port ] < ---[        ]< ----[  外部  ]
    [ 不正な Program ] [ port ] < ---[FireWall]< ----[  外部  ]
    [ 不正な Program ] [ port ] < ---[FireWall]< ----[  外部  ]

Block を指定する単位は port, Program で指定できる
  
* PC 間の通信と Security 対策 同じ PC ならば問題にならない * TCPServer の listen で問題になる * 外部との境界を流れるデータを監視 & 不正なアクセスを検出・遮断 * 127 から始まる id は block する必要なし // ここが よいかも こうやって指定します
  // 指定した Port を例外にする 
  CP > Windows > FireWall


* Registry に値が設定される * 40000 - 40100 Port ( UDP TCP )とかいう * 全般 tab でファイアウォール機能 の ON/OFF を選びます
  [全般] タブ 
      [有効] 
          Windows ファイアウォール機能を有効に設定します
          外部からの不正アクセスを防いでくれます
           D: 有効


        [例外を許可しない][例外] タブ (下記参照) にて登録した設定を無効にします

      [無効] 

        機能を無効にする
        ほかのファイアウォール ソフトがインストールされている場合を除き
        できれば設定はやめた方がいいです
        ( 外部からの不正アクセスに無防備になります )
* 機能を ON にしたまま 自分のプログラムは Block しないようにできます
  [例外] 
    指定した Program, Port をブロックしないでね ! と伝えます
    

(例:ネットワーク ゲームやファイル転送など) 
ただし [全般] タブにて [例外を許可しない] を有効にした場合
 [例外] タブの設定は反映されません

この一覧に表示されたプログラム, ポートは
それぞれの項目において
通信の許可, 不許可の設定やスコープの変更ができる
 




  [プログラムの追加] 
    追加したプログラムによる入力方向のネットワーク接続をブロックしません
    セキュリティ上の危険があるので確認の上追加すること
    ( 入力とは recv ってことか ? )


  [ポートの追加] 
    特定のポートにおける外部からのアクセスをポート番号, プロトコルを選択して許可する

  スコープの変更では
  ポートまたはプログラムをどのコンピュータに対してブロックを解除するかの指定も行えます
  ポートを追加することで
  外部からの不正なアクセスなどに対して無防備な状態になり


  [編集] 
    登録すみの内容を修正します

  [削除] 
    登録したものを削除します
[詳細設定] * ネットワークごとに 設定をしたいときはここ ( 例: LAN のみとか ) * 他には LOG をとったり 規定値に戻したりとか 複数のネットワーク接続がある環境でネットワーク接続別の設定 パソコンに対しての接続や [ネットワーク接続の設定] 特定のネットワーク接続に対して例外の追加や詳細設定をする 今回したのはこちら インターネットのユーザーがアクセスできる Network で実行中のサービスを選択してください WebServer ( HTTP ) [セキュリティのログ] ファイアウォールによって許可された接続やブロックされた接続に関する情報を記録します [ICMP] ファイアウォールが有効でも トラブルシューティングに使用するユーティリティ(PING コマンドなど) を使用できるように設定する POINT PING も network を使った Program [既定の設定] Default 設定を戻したいときはこちら
___

■ JSP


  POINT
    JSP が処理された結果は, HTML になる. ( RMB > ソースを表示 で対応 ) 

  使いどころ
    顧客の情報を集めたい -> DB との連携
     サーバが実行できるプログラム
     < %%> タグを用いることで、HTML の中にプログラムを埋め込むことができる
     CGI とは異なり, ps 起動のオーバーヘッドがない
       -> 要は, Server( HTTPServer ) が他の ps を起動している. 
     Java の文法で記述でき、Java の利点を生かしたシステム構築ができる
     ASP とは異なり, 各種環境で動作する. 
___

■ 環境設定

___

■ JSP Program の基本

___

■ Tutorial

    // test.jsp
  < font color=red>  // 文字コードの指定とか < /font>
    < %@ page language="java" contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
    < %@ page language="java" contentType="text/html; charset=SJIS" pageEncoding="Windows-31J" %>
    out.println( request.getParameter( "param" ) );     // RET: B&B

    // Browser の URL 入力
    [ http://localhost/app/test.jsp?param=B%26B ]

簡単な JSP Sample

 < html>
 < head>< title>Hello World!< /title>< /head>
 < body>
 < h1>Hello World!< /h1>
 < p>

     # Java の実行結果におきかわる
 < %

     // ここに Java Code をかく
     String hello = "Hello World!";
     out.println(hello);
 %>

 < /p>
 < /body>
 < /html>
___

■ 暗黙オブジェクト

以下のオブジェクトは JSP 内で明示的に宣言しなくても利用できる。 request out
___

■ Javascript.との違い

POINT 違いをみるには, Browser でソースを表示をするとわかる jsp 側では HTML の記述のみが表示
    // JavaScript Sample
 < html>
 < head>< title>Hello World!< /title>< /head>
 < body>
 < h1>Hello World!< /h1>
 < p>
 < SCRIPT Language="JavaScript">
 < !--
     hello = "Hello World!";
     document.write(hello);
 // -->
 < /SCRIPT>
 < /p>
 < /body>
 < /html>
[ Browser ] ---( リクエスト ) ---> [ Server ] [ Browser ] < ---( JavaScript を含んだ HTML ) --- [ Server ] JavaScript に向いていること
    Client で処理が完結すること. ( Game, Animation )    
JSP に向いていること
   Server 側にある DB にアクセスすること
   Server 側で処理をするので, Java の実行環境がなくても( 携帯 )実行が可能
___

■ Class.を利用する

DESC Java でつくった Class を JSP から使える 設定ファイル web.xml , context.xml の指定は不要 つくりかた
    1. Java Class をつくる ( Java と同じ構文で OK )
    2. Compile して指定場所に配置する
    3. JSP から class を import して利用する
JSP から利用できるクラスは次の条件が必要
  // public 属性にする 
  1. VarMem
  1. MethodMem

  // Public Class
  public class Test {
    public String func() {
      return "test class";
    }
  }

ここに置く


    /tom/app/WEB-INF/classes/testpkg/Test.java
    /tom/app/WEB-INF/classes/testpkg/Test.class
    /tom/app/testcalljava.jsp


      Package 名 : testpkg
      Class   名 : Test

      /TOMROOT/app/WEB-INF/classes/testpkg/Test.class 
      

      $docBase/WEB-INF/classes/namePkg/original.class 

* JSP からよびだします
      // import 宣言をします
      < %@ page contentType="text/html; charset=sjis" import="testpkg.Test" %>

      < %
          Test t = new Test();
          out.println( t.testfunc() ); 
      %>      
/* testcalljava.jsp */ # ( $docBase とは jsp のある dir と同階層にする必要がある ) # examples 相当 $TOMCAT/testdir/WEB-INF/classes/atmarkit/CopyrightHTML.class useclass.jsp POINT
    WEB-INF/classes/ 以下にクラスを配置することで
    新しく作成したクラスは Tomcat を再起動なしで読める
    もともと存在しなかった WEB-INF Directory を新しく
    作成したときはパスが認識されないことがある
    そのときは Tomcat を再起動する

    WARNING
      class を再コンパイルしても反映されないかも
      再起動すれば反映する
___

■ 自作.Bean.の作成とその使用

DESC Bean とは JavaBeans の仕様にのとった命名, 設計規則 Java のクラス Rule を準備するだけで, 特定のクラスをBeanとして扱えます うまく使用することで、開発効率を向上させたり、 より信頼性の高いプログラミングができる Java 言語を用いて開発され 部品化されたプログラムを組みあわせてアプリケーションソフトを構築する手法 Java言語でBeanを作成するための技術仕様 プログラムを部品化すり 複数の開発者間での共有やコードの再利用が容易になり また出来合いのBeanやサードパーティから提供されるBean を組み合わせてアプリケーションソフトを開発すり 開発効率が飛躍的に向上します こんなルールあります
   プロパティの値を設定するメソッドとプロパティの値を取得するメソッドの名前に決まり事がある
        Accessor ( setとgetの後にプロパティ名を続けたメソッド )を準備するだけ
Bean を利用する場合は異なる tag ( BeanTag )を利用する. < jsp:useBean>  Bean の使用を宣言 # atmarkit.CopyrightHTMLBean クラスのBeanをcopyrightという名前で使用することを宣言しています。 # Program でいうと, atmarkit.CopyrightHTMLBean copyright = new atmarkit.CopyrightHTMLBean(); < jsp:useBean id="copyright" class="atmarkit.CopyrightHTMLBean" /> < jsp:setProperty>
 Bean のプロパティの値を設定する
   // copyrightという名前で宣言された
   Bean の year というプロパティに 2001 という値を設定
   // 具体的には、copyright オブジェクトの setYear メソッドが実行されるので、 
   // 通常のプログラム: copyright.setYear("2001"); 
   < jsp:setProperty name="copyright" property="year" value="2001" /> 
< jsp:getProperty>
 Bean のプロパティの値を取得
    // copyright という名前で宣言された Bean の shortHTML というプロパティの値を取得
    // copyright オブジェクトの getShortHTML メソッドが実行される

    // 通常のプログラムにおける、copyright.getShortHTML(); 
    < jsp:getProperty name="copyright" property="shortHTML" /> 

    次のように考えれば OK.
    InstanceName + get + property < -> copyright.getShortHTML();
Bean のスコープ 今回は、1ページの表示で完結するJSPプログラムだったので 特に意識しませんでしたが Bean にはその有効範囲を指定できる scope 属性があります 種類は page : 現在の page DEF request session application < jsp:useBean> タグを次のように記述することでscopeを指定できます
    < jsp:useBean id="id_name" class="class_name" scope="scope" /> 
    
    Scope とは. 
      一度宣言された Bean にアクセスできる有効範囲を意味する.
scope 属性は EX scopeにsessionを指定した場合、同じセッションの範囲においては 違うページであっても同じidの値を指定することで、 同一のBeanにアクセスできる このようにすることで、セッション間の各ページで1つのBeanを共有できて 異なるページで同じ情報へアクセスできます   それぞれのscopeを指定したときのBeanの特徴は次のようになります scopeの指定によって、Beanをより有効に利用できるようになります。 scope アクセス可能な範囲 特徴 page 現在のページのみ デフォルトで使用されるスコープ もっとも有効な期間が短い request 現在のリクエストの範囲(インクルードされたページ、転送先のページを含む) レスポンスがユーザーに返されるまでの間だけ有効 EX: form で入力されたデータを保持させる session 現在のセッションの範囲 セッションと同じだけの有効期間を持つ EX: ログインしたユーザーの情報を保持させる application 現在のWebアプリケーション アプリケーションの有効期間と同じだけ永続的に使用できる EX: アクセスカウントなどアプリケーション間で共有する情報を保持させる
___

■ クッキー(cookie)を使う

DESC 前回は JSP でセッションを扱う方法を説明しました。 JSP のセッションでは セッションの識別 ID がクッキーとして自動的にブラウザ側に格納された POINT
    Cookie とは
      Server から渡される設定 file ( NAME = VAL で構成される )

  なぜ Cookie ができたか ? 
    Server で 情報を提供できるが, User の端末に情報を記述できなかった.     
    というわけで設定 file を渡そうということになった. )

    [ Browser ] < -> [ Server ]
                < --- 書き込みができない. 
JSP から Cookie の値を取得できる API がある Cookie をどうやってわたすか ? クッキーがサポートされるブラウザへ Server が小さなデータ(name=valueの組み合わせ:クッキー)を レスポンスのヘッダ部分に記述することで保存させる -> Response にも Header がある.( ということは相互に HTTP 通信. ) -> Browser が Cookie を利用できるようにしなければならない.
    shell>  curl -I http://www.google.co.jp | grep "Set-Cookie"

    // RET
    Set-Cookie: 
    PREF=ID=1a1244830643bcfa:FF=0:TM=1320792924:LM=1320792924:S=1bEhuL4795SjGXSc; 
    expires=Thu, 07-Nov-2013 22:55:24 GMT; path=/; domain=.google.com
Cookie を Browser から渡すには Browser は次回そのクッキーを発行したサイトに訪れた際に 持っているクッキーをサーバに提出 サーバはその内容を確認して, 前回自分がブラウザとやりとりしたデータを復元できる クライアント側がクッキーを受け取った後は、 ブラウザを閉じたり、コンピュータの電源を切ってもクッキーの情報を再度利用できる クッキーには有効期限を設定できるので 特定の期間を過ぎたクッキーを無効にできる
  [ Browser ] --------> [ Server ]                  
              < --------
              RESPONSE
                name=key1
                name=key2


              REQUEST
                name=key1
                name=key2
  [ Browser ] --------> [ Server ]                  
              < --------
Cookie が利用されるケース
    会員サイトのユーザーIDを保存して、次回の訪問でユーザーを認識する

    最後に訪れた日時を保存して、ユーザーの訪問頻度を計る

    ユーザーがカスタマイズしたサイトの情報(ユーザーの好み)を保存、次回もその設定を適用する


    掲示板へ投稿するときに入力した名前とメールアドレスを保存しておき次回の入力の手間を省く
ページにアクセスすると, アクセスした時刻が Cookie として渡される

  < %@ page language="java" contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
  < %@ page import="java.net.*, java.util.Date" %>
  < %
 
    // 現在の時刻を取得
    Date now = new Date();
 
    // クッキーに格納する文字列を作成(URLエンコードをする)
    String value = URLEncoder.encode(now.toString());
 
    // 名前が"accesstime"、値が現在時刻であるクッキーを作成
    Cookie c = new Cookie("accesstime",value);
 
    // 有効期間を1週間に設定
    c.setMaxAge(7 * 24 * 60 * 60); 
 
    // クッキーを追加 ( Http Response Header に追加される )
    response.addCookie( c );

  %>
POINT Cookie を破棄する方法はないため 期間 0 の Cookie を再発行して上書きする

< %@ include file="util.jsp" %>
< %@ page language="java" contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
< %@ page import="java.net.*, java.util.Date" %>

 < %
 // クッキーを使用する(クッキーを破棄する)
 
 // クッキーを作成
 Cookie c = new Cookie("accesstime","");
 
 // クッキーの有効期間を0秒に設定
 c.setMaxAge(0);
 
 // クッキーを発行
 response.addCookie(c);
 %>

 < pre>
 クッキーの内容を削除しました
 < a href="cok.jsp">クッキーの内容を確認する< /a>
 < /pre>

___

■ 別サイトへの転送(Redirect)

DESC forward アクションを用いて、アクセス先を転送できる 特定の処理の前 または処理の途中で条件に応じたアクセス先の振り分けに使う POINT forwardアクションの用途は 特定のJSPのページから別のページへアクセス先を転送できる 転送先のページには別の JSP 以外にも, HTML, CGI のページも指定できる  代表的な例 UserAgent ( クライアントのブラウザの種類 )によるページの振り分け
  // 転送処理をしないと if で処理わけをします 
  // 煩雑になります
  //
  if ( PC からのアクセス ) {
    // PC HTML 出力
  }
  else {
    // 携帯 HTML 出力
  } 


  // 転送処理ができれば, 別ファイルを用意すれば良い
  if ( PC からのアクセス ) {
    // PC Page へ転送. pc.jsp 
  }
  else {
    // 携帯 Page へ転送. keitai.jsp 
  } 
page Directive ( page に対する指示 )
  < %@ page contentType="text/html; charset=Shift_JIS" %>

  < %
  // request から Browser 情報を取得する
  String s = request.getHeader("user-agent");



  // そして Browser によって別ページへ誘導する
  // forward アクションは、転送先を ( ローカル URL ) しか指定できない。
  if ( s.indexOf("Opera") >= 0 ) {
    response.sendRedirect("http://yahoo.co.jp");
  }
  else {
    response.sendRedirect("http://google.co.jp");
  }

  %>
転送元と転送先での変数の共有 転送処理を行うと便利なケースのもう1つの例としてエラー処理があります EX ユーザーから情報を入力してもらうような場合 必ずしもその入力が完全であるとは限りません 入力内容をチェックし、正しければそのまま処理を続け、 問題がある場合にはエラーページへ転送することで効率の良いプログラムができます  このような場合 転送先のエラーページでは、エラーの内容を表示するのが一般的 このためには エラーの情報が転送元のページからエラーページへ渡される必要がある  forward アクションによる転送では転送元と転送先で、 session オブジェクト、 request オブジェクトが共有される 従って これらのオブジェクトに変数を格納することで情報を持ちまわることができる 転送元と転送先がどちらも同じアプリケーションに属する場合は、 application オブジェクトも共有されるため、 このオブジェクトに変数を格納することもできます
___

■ DB.へアクセスする

DESC 業務アプリケーションの世界では、 顧客情報や商品情報( Tool の統計情報. ) などを格納した DB とJSPの連携が必要になることが多い PostgreSQL のセットアップ JSP からデータベースへアクセスするためには、JDBC ドライバ が必要になります。 -> JavaDataBaseContainar テーブルの作成 PostgreSQLのセットアップが完了したら、 データを格納するためのテーブルを作成する テーブルの作成は、SQLの create table 文でする table をつくります
  create table member (
      member_id     serial         primary key not null,
      name          varchar(32)                not null,
      kana          varchar(32)                not null
  );  
データベースアクセス用クラスの作成( java で実現する ) JDBC とは JDBC とは Java から DB にアクセスするための API
___

■ SYNTAX

___

■ Directive

SYNTAX < %@ %> DESC JSP Container に Page を処理する方法を指示
    // import Directive
    < %@ page ... %>

    import "java.util.*";
    < %@ page import="java.util.*" %>
    < %@ page import="java.lang.String" %>
    < %@ page import="java.io.*" %>

    # 式  出力文を生成するのに使用
    # Java Code を記述していることを忘れずに. 
    < %= "foo" %> == < % out.println("foo"); %>

   // 日本語をつかうときは, どの文字Code で出力するか指定します
   // Browser が文字化けしないならよいですが、 基本は指定します

   // JSP での出力結果を指定する
   // Default は ISO-8859-1 
   // JSP の処理結果であって
      日本語< br> と記述した場合は Editor での文字 Code になる
   // 現時点では正しく動作しない. -> ないと正しく動作する.

   // EUC で出力
   < %@ page contentType="text/html; charset=euc-jp" %>

   // SJIS で出力
   < %@ page contentType="text/html; charset=Shift_JIS" %>

   // MIME Type を指定する

   // JSP で 特殊文字が文字化けする場合の対処方法
   < %@ page contentType="text/html; charset=windows-31j" %>

String はなくもて利用できる # 適切な module を import しない ERROR になる. # スクリプトレット # 実際の処理の内容を指定のスクリプト言語( デフォルトでJava言語 )で記述 # [;] 必須. < % out.print(nowTime); %> # Package の使用を宣言 # Package を使用すると, namespace みたいなもの. # == import "java.util.*"; < %@ page import="java.util.*"%> # テキストの出力部分とロジック部分を混在させることも可能. # Demerit として, ロジックとHTML部分が混在し、メンテナンス性が低下 # -> String に出力結果を格納して, 最後に, < %= message %> とすべし. Directive tag は< font color=red> // < /font> とは無関係. < %! %> 宣言 この宣言文を含むJSPページに固有の変数とメソッドを定義するのに使用します User の入力をうける. # これ経由でないと, 送信先で正しく開くことができない. # ie. 直接 /form.jsp を指定することはできない. < form action="form.jsp"> // 文字コードの変換を行う // EUC_JP として渡された Code // Java の内部 Code : Unicode name = new String( name.getBytes("8859_1"), "EUC_JP" ); birthday = new String(birthday.getBytes("8859_1"), "EUC_JP");
___

■ 関数(Function)

SYNTAX < %! function(){} %> DESC JSP 内で利用する関数の定義
    < %!
      // この Directive 内に関数を定義する

     // フォントカラーの指定を行ったHTML文を作成する
     public String getColoredHTML(String str, String color) {
        return "< font color=\"" + color + "\">" + str + "< /font>";
     }

     public int add( int x1, int x2 )
     {
       return x1 + x2;
     }
    %>

    < %
        // 呼び出す
        out.print( add( 1, 2 ) );
    %>
___

■ Include

SYNTAX // 静的 include < %@ include file="include.jsp"%> DESC * JSP でも Include できます 1. Code 量を減らすため. 2. Maintenance 効率をあげるため JSP でのプログラムの再利用方法
   インクルードディレクティブを使用した静的インクルード
   インクルードアクションを使用した動的インクルード

   オリジナルクラスの作成
   オリジナル Bean の作成
flush 属性 インクルードされるページを読み込む前にページの 出力バッファをフラッシュするかどうかを制御 現バージョンのJSPでは、必ず true に設定することになっているので、 インクルードアクションを使用する場合は、flush="true" を必ず記述するようにします # < jsp:include page="include.jsp" flush="true" /> インクルードディレクティブ jsp code を埋め込んでから, CMP . そして 実行. # Servlet の本質は Program を走らせて, Redirect することかも. [ sinc.jsp ] ---> ( include( 埋め込み ) ) ---> [ Compile ] ---> ( class file == Servlet )
___

■ IncludeAction

DESC 実行時に 組み込んで, 結果を埋め込む. 要は Action を組み込む どういうときに利用するか ? 1. 特定のフォーマットで Copyright を表示するプログラムを再利用する [ ainc.jsp ] ---> [ Compile ] ---> ( include( 埋め込み ) ) ( class file servlet ) 2 回 Compile されている. 1. include.jsp の Compile 結果を include ファイルパスの指定方法 Include する際の, path は JSP file からの相対. 絶対のどちらも可能. 同一の Context にある必要がある. BAD # context.xml での appBase が基準. < jsp:include page="/home/username/jsp/include.jsp" flush="true" /> # url の指定もできない. < jsp:include page="http://localhost/jsp/include.jsp" flush="true" /> # Relative OK # Relative < jsp:include page="../include.jsp" flush="true" /> # absolue < jsp:include page="../include.jsp" flush="true" /> Java コードの比較 JSPプログラムがサーバ側でJavaコードに翻訳されるときにはどのような 処理がなされるのでしょう。 本連載の第4回で“Hello, World!” の文字列を表示するJSPプログラムがJavaコードに翻訳された結果を見たように、 今回も5-1.jsp、5-2.jspがJavaコードに翻訳された結果を見てみましょう Javaコードは非常に長いので、実際にHTMLを出力する部分だけに注目してみます。
___

■ 基本事項

# servlet を拡張したもの.

 // Servlet をつかった hello world Sample 
 // 見てのとおり メンドイです
 import java.io.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 

 public class HelloWorld extends HttpServlet {
 
   public void doGet(HttpServletRequest request,
    HttpServletResponse response)
       throws IOException, ServletException
   {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("< html>");
      out.println("< head>< title>Hello World!< /title>< /head>");
      out.println("< body>");
      out.println("< h1>Hello World!< /h1>");
      out.println("< p>");
      out.println("Hello World!");
      out.println("< /p>");
      out.println("< /body>");
      out.println("< /html>");
   }
 }    
___

■ Server側のJSPの処理

    1. Request をうける. ( Socket 通信かな ?  ) 
    2. jsp を Compile する. 
        foo.jsp ---> foo.java ---> CMP
                    ( $tomcat/work/ に生成  )

    3. 
    [ Browser ] ---> [ Server ] -(Compile)-> 
Context Path に jsp というパスを追加して、 その中に hello.jsp という名前でファイルを保存した場合 localhost_8080%2Fjsp/_0002fhello_0002ejsphello_jsp_0.java という名前で、このソースコードが存在する 自分の環境では, $tomcat/work/Catalina/localhost/my/org/apache/jsp/test_jsp.java -> Java Code が生成されている. 文字列も Java で記述するのか 変換される内容
    HTML 文字 < -> out.write( "html" );
    1. ImplicitObject [ out ]
        JspWriter out = out = pageContext.getOut();

    1. JSP ページによって生成されるクラスは HttpJspBase クラスを継承する 
          -> JSP は 動的に Servelet を生成する仕組み. ( ie. Servelet に集約する. )

    HTML 文の出力は HttpJspBase::_jspService() メソッドでされている
    HTML 文の出力 JSP ページで記述した処理は
    try{ }catch(Exception ex){} 構文の中でする
___

■ HTTPHeaderの情報

DESC HTTPHeader とは Client と Server が Data をやりとりする際の付加される情報 荷物でいうところの 宛名、差出人 クライアントが利用しているブラウザソフトの名称やクッキー情報 ホスト名などが含まれる
    // Client がだれか調べて,  携帯用のコンテンツに出力もできます
    String user_agent = request.getHeader("user-agent"); 

    // 接続元の IP をえる
    out.print( request.getRemoteHost() );

    // Header の項目をすべて取得する
    out.print( request.getHeaderNames() );

    // getHeader() に渡せばすべて取得できる 
___

■ HTTP





___

■ リクエスト(Request)

HTTP プロトコルではクライアントは GET メッセージでネットワーク先のリソースを取得する。 SYNTAX GET / HTTP/1.0 option ... パスとバージョンの後に、0個以上の任意のオプション行をいれる。 空行でリクエストの終了を意味する。
    ---------------------------
    GET リソースパス バージョン
    オプション1
    オプション2
    ...

    ---------------------------
一番シンプルなメッセージは以下になる
    GET / HTTP/1.0\r\n\r\n
オプションは 属性名: 値 で指定する。 Host を指定する
    GET / HTTP/1.0
    Host: ooo.iiyudana.net

プログラムでは以下の文字列を send する。
    const char *msg = "GET / HTTP/1.0\r\nHost: ooo.iiyudana.net\r\n\r\n";
    send( sock, msg, strlen(msg), 0 );
___

■ リクエストオプション

___

■ Host

仮想ホストによってひとつの IP に複数のホスト名を対応づける場合に クライアント側がどの URL のページを要求しているか指定をする。
    xxx.xxx.xxx.xxx < -> host1.com
                        host2.com
                        host3.com
クライアントが GET / HTTP/1.0 を要求しても サーバは host1.com/ host2.com/ host3.com/ のどれを返せばいいかわからない。 そこでクライアントは host1.com/ に対応づいたページが必要は場合は Host: host1.com とする。
___

■ レスポンス

POINT HTTP ではリクエストの結果が成功なのか失敗なのかは 最初の一行に書いてある

  // 200 : StatusCode ( Success ) 
  // OK  : 適当な Message ( OKDayo ! )でもいい 
  HTTP/1.0 200 OK
    
  // 404 : そんな file ありません  
  HTTP/1.0 404 NOT FOUND

Webブラウザーなどで HP を見るときに使用するプロトコル ---------------------------------------------------------------- // 結果 HTTP/1.1 200 OK // 更新日 Last-Modified: Sun, 19 Mar 2000 07:16:23 GMT // ヘッダ Content-Length: 1484 Content-Type: text/html ( 空行 ) ---------------------------------------------------------------- // 空行をはさんでデータ本体がきます < HTML> 〜 < /HTML> ----------------------------------------------------------------
___

■ JSP.文字コード



この文字列がPOSTのボディ部分
( POST は Body に組み込まれる )
 



  日本語の場合は歴史的な経過から文字エンコーディングが複数存在する
  現在はMicrosoftが開発しWindows系で一般的に採用されている 
  Shift_JIS と呼ばれるもの

  POINT
    win32 環境では SJIS が一般的

    Linux系で一般的に採用されてい る
    EUC-JP の2つのエンコーディング双方が利用

    WebSphere はShift_JIS がデフォルト

WTE のサーブレット・エンジンもクライアントから
    URLエンコードで送られてきたパラメタ(バイト列)を

    Shift_JISの文字コード列だと見なしてこれをJavaのUnicodeに変換する

    WTE の環境では
    クライアントがEUC-JPで要求パケットを送ってくると問題


    WebSphere のアプリケーション・サーバでは設定ファイルの
    デフォルトの
    クライアント・エンコーディングを変更してこれに対応することになる

    これ以外にもPOST要求でHTTP要求のボディ部分に
    クライアントからのパラメタを書き込ませ

    これをバイトごとに読み出してEUC-JPとしてUnicodeに変換する手段もあるが一般的ではない
     WTEの環境ではクライアントから必ず
      Shift_JISでエンコードされた要求が到来するようにすることが推奨される

      サーバに情報を送信するためのフォームを含む HTML ドキュメントが表示され
      ユーザがその画面の送信ボタンをクリックして必要な情報をサーバに送信する際


  POINT
     IE, Netscapeのブラウザは
     HTML文書の文字コードを使ってバイト列を作成し、URLエンコードする


  クライアントに渡すフォームを含む
  HTMLドキュメントには以下の要素を含めることが必要

  これは SJIS でかかれてまよ ! と宣言する
  そして SJIS として URLEncode される
  < META HTTP-EQUIV="Content-Type" CONTENT="text/html;charset=Shift_JIS" >


  更にサーブレットからそのような画面をクライアントに返すときは
  エンジンに変換する文字エンコーディングを明示的に指示するだけでなく
  クライアントに
  HTTP 応答パケットのヘッダ行で使用文字エンコーディングをはっきり通知すべき

  response.setContentType("text/html; charset=Shift_JIS");  // 応答ヘッダContent-Type行追加



  Websphere のエンジンはShift_JISでエンコードされた要求パラメタは正しくUnicodeに変換してくれている

  Tomcat のエンジンはそれほど親切ではない

  このサーブレットは
  Tomcat のコンテナで走らせると漢字は正しく表示されない

  
  変換する前の文字コードは ISO8859_1(Latin-1) であると仮定して
  8859_1 から Unicode への変換テーブルを用いて
  String 型 のオブジェクトにしてしまう

  ここが Java の Default の操作


  POINT
    JAVA は URL を decode するときに "8859-1" として仮定していること -> それを UNICODE に変換する

  Byte --> 8859_1 --> request.getParameter( "xxx" )  
  --->   getBytes( "8859-1" ); ---> 指定した文字コードに変換しろ  となる

      ( Java の勝手な解釈 )                         もう一度 バイトコードをくれ getBytes( "8859-1" ) これをもとに  

Shift_JIS や EUC-JP の文字列が URLエンコード されてくると
対応した 正しい Unicode の文字列が得られない

 
String correctName = new String(name.getBytes("8859_1"), "Shift_JIS");






// 指定文字コードに対応した Byte 列をかえす
getBytes(String charsetName) 
指定された文字セットを使用してこの String をバイトシーケンスに符号化し
結果を新規バイト配列に格納します



  // HttpRequestDumpの要求パラメタの出力部分を
  // Tomcat用に変更する

  out.println();

  out.println("要求パラメタ:");

  // すべての Parameter を列挙する
  Enumeration paramNames = request.getParameterNames();

  while ( paramNames.hasMoreElements() ) {

    String name = (String) paramNames.nextElement();

    String[] values = request.getParameterValues( name );

//  out.println("    " + name + ":");

//  Tomcatの場合は上の1行は以下の2行のように変更し
    日本語の文字化けに対応すること

    // TomcatはURLエンコードされているパラメタのコードが
    // 標準のISO8859_1(Latin-1)であると仮定し
    // それを単にStringに(つまりUnicodeで)格納して
    // 各サーブレットにわたす

    //  そのためパラメタ文字列を取得するときに 
    // ISO8859_1から自分が期待する

  WARNING 
    短いデータではJISAutoDetectは正しく機能しない

    String correctName = new String(name.getBytes("8859_1"), "Shift_JIS");

    out.println("    " + correctName + ":");

    for (int i = 0; i <  values.length; i++) {

        try{

//          out.println("      " + values[i]);

//  Tomcatの場合は上の1行も同様に以下の2行のように変更すること

            String correctValue = new String(values[i].getBytes("8859_1"), "Shift_JIS");

            out.println("      " + correctValue);

        }catch(Exception e){

            System.out.println("URL Decoding Error");

        }

    }

  }
// これは失敗する 8859_1 という文字セットは8ビット単位なので Unicodeの1文字は確実に8859_1の1バイト (null(%00)も含めた256文字)に1対1で対応しており問題が生じなかった WTE の SJISコンバータは 変換できないバイトは無視してしまうので そのような逆操作をしても元には戻らない
___

■ Network基礎知識


___

■ 名前解決

DESC TCP/IP 通信では IPアドレスが分れば通信はできる。 しかし数字で表すため、覚えずらい。 また IP はホストが所属するネットワークに関係しているため移動すると値が変わってしまう。 そこで、名前をつけておくことで 覚えやすく、変更があった場合でもクライアント側が代えなくても良いようにした。 携帯の電話帳の名前から電話をかけるのと同じ。 あくまで IP による通信は全てIPアドレスを指定する。 InterNetwork だけではなく, LocalAreaNetwork でも必要です POINT 索引をする方法はこんなものがある
    IPAddress を知ることが, 目的で DNS はそのひとつにしかすぎないこと
    WindowsNetwork では Computer名をつけてます

  // InterNetwork での名前解決
   DNS

  // LocalNetwork にはこんな仕組みがある


   BroadCast         // 全員に呼びかけ
   LMHOSTS/HOSTS  // 設定ファイル方式
   WINS
___

■ DNS

DESC Internet でつかわれてる 名前解決方法 ホスト名(www.google.co.jp)とIPアドレスの対応表を管理した分散データベースのこと。 DNS(DomainNameSystem)Serverが.Host名.をIPAddress.を変換する。 LAN で DNS による名前解決 をするためには 自前で DNS Server を用意する Network 内の IPAddress < -> PC名の対応表をもたせれば良い Domain を取得していないときは 慣例的に [ local ] とつける 名前が foo だったら, 完全修飾ドメイン名 は foo.local POINT PC名 + DomainName となる TCP/IP 通信では 相手の IP がわかっていれば通信できる。 でも数値ではわかりずらい そこで名前( DomainName )とIPを関連づけようということになった。 この仕組みを DNS と言う。 POINT
    :: 数値では 指定しずらいので 名前をつける

    11.123.123.11 < -> nantoka.com

    // 電話番号の変換と同じです
    090-xxxx-xxxx < -> 佐藤さん
dot 表記とは www.so-net.ne.jp www : Domain 取得者が自由につけて良い so-net: Domain 取得者が自由につけて良い ne : 組織の種類 www.so-net.com gTLD : generic Top Level Domain ( だれが取得しても OK ) DomainName には 商標権 は配慮されない. * DNS Server に問い合わせている * 近年では、日本語など各国独自の言語 文字でドメインを登録できる国際化ドメイン名も利用できるようになった DNS とは * 全世界のDNSサーバが連携して運用する * 一つのドメインに複数のIPアドレスを対応させたり 一つのIPアドレスに複数のドメインを対応もできる * DNSServer は自分が管理するネットワークの IP < -> DomainName を相互変換する   実世界の住所のように階層になっていて[ . ]で区切って表す。

  // 一番が右が TOP です
  [ThirdLevelDomain][SecondLevelDomain][TopLevelDomain]

Domain には種類がある

  // 国ごとにわりあて
   ccTLD(country code TLD)

  // 国や地域とは関係なく別の基準でわりあて
   gTLD(generic TLD) 

  トップレベルドメイン ドメインが重複しないよう、ドメイン管理はICANNという組織が一元管理してます ICANNから委任を受けた各国の機関が割り当て業務をしてます 日本の ccTLD であるJPドメイン(.jp)はJPNICが管理する TIP Internet, Mail は WebServer, MailServer と[ 通信 ]することでできる 階層構造の電話張( 関連つけ ) XXX さんは < -> 090-xxxx-xxxx foo@bar.co.jp [jp] を管理している. DNS Server に IP 問い合わせると答えを教えてくれる @ の後が DomainName ( Computer のこと ) DomainName < -> IPAddress ( Computer に対して 一意につけられるアドレス. ) nslookup tracert www.google.com tracert www.google.com -> stracert 210.111.222.333 と変換 Domain 名 == LAN 名 自分の IP 情報を見てみる > ipconfig /all

Windows IP Configuration

        Host Name . . . . . . . . . . . . : xxx
        Primary Dns Suffix  . . . . . . . : 
        Node Type . . . . . . . . . . . . : Unknown
        IP Routing Enabled. . . . . . . . : No
        WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter ローカル エリア接続:

        Connection-specific DNS Suffix  . : 
        Description . . . . . . . . . . . : Realtek RTL8139/810x
                                       Family Fast Ethernet NIC
        Physical Address. . . . . . . . . : xx-xx-xx-xx-xx-xx
        Dhcp Enabled. . . . . . . . . . . : Yes
        Autoconfiguration Enabled . . . . : Yes

       // 自分の IP です
       // Private Address です
        IP Address. . . . . . . . . . . . : 192.168.0.2
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . : 192.168.0.1
      
        // DHCP サーバの IP Address 
        DHCP Server . . . . . . . . . . . : 192.168.0.1

       // DNS Server の IP Address  
        DNS Servers . . . . . . . . . . . : 192.168.0.1
        Lease Obtained. . . . . . . . . . : 2010年4月29日 6:49:22
        Lease Expires . . . . . . . . . . : 2010年4月30日 6:49:22

nslookup で DNSServer に問い合わせて, FQDN を IP Address に変換します

  // google.co.jp の IPAddress を教えて !
  nslookup google.co.jp

[ . ] は 名前の階層だった. DNS に対して責任範囲を限定させる DNS suffix = local ( 設定されている ) 名前とIPアドレスの変換は DNSサーバに名前に対応するIPアドレスを質問します DNSサーバは、問い合わせを受け取ると対応した対応するIPアドレスを教えてくれる。 教えてもらったIPアドレスで通信できる。 しかし ひとつのDNSサーバでインターネット上の全ての名前と全てのIPアドレスを一箇所で把握するのはムリ。 そのため、名前情報を一箇所で集中管理しなくても良いように、 DNSは自律分散協調するシステムとしてできている。 具体的には [ 名前に階層を持たせ ]て各DNSサーバに各自の守備範囲を持たせている
           [ RootDNS ]
               |
         [jp]  [net]  [com]
                |
          |      |
       [co.jp]   [or.jp]
         |
     [yahoo.co.jp]  [google.co.jp]
こんな順番
    // xxx.co.jp の IPAddress を知りたい!

      
      1. 自分の所属する Network 内の DNS Server に聞く
      2. わからないなら, ROOT JP に聞く
      2. co.jp にきく
      3. xxx.co.jp にきく
      4. わかった!

___

■ Broadcast

DESC Network 上のすべての PC に一斉に呼びかける。 そして、呼ばれたコンピュータは自分の IPAddress を返す。( 返事をする ) WindowsNetwork で NetBIOS over TCP/IP が ON になっているとこの手法が使える。 呼びかける 名前は TCP/IP のホスト名ではなく, DNFQ ではなく, NetBIOS 名です WARNING 同一 SubnetMask しか使えません
___

■ LMHOSTS/HOSTS

DESC LMHOSTS.SAM という テキストファイルに IP < -> PC名 という情報を記述しておく。 TCP/IP Property > [WINS] WARNING 次のようなデメリットがある
    すべての PC に用意する必要がある。
    変更、追加があるとそのたびに追加しないといけない
___

■ WINS(WindowsInternetNamingService)

DESC DNS と同じく , IPAddress < -> コンピュータ名を変換する方式 * 違うのは DNS が HostName + DomainName で Computer を管理して WINS は NetBIOS名 で管理すること 名前解決 サーバに再帰的に問い合わせて結果を得るというシステム 例えば example.comというドメイン内のあるユーザーが www.foo.co.jpというホストのIPアドレスを探す場合 まず最初はexample.comのDNSサーバに問い合わせる しかしこのDNSサーバは、example.com の内部のホスト名しか管理しておらず 他のドメインの情報は持っていない そこでこのDNSサーバは、DNSの階層構造ツリーを逆にたどって まずjpドメイン内のホストを管理しているDNSサーバを見つける 次に、そのサーバを使って、co.jpドメインを管理しているDNSサーバを見つけ さらに foo.co.jp を管理しているDNSサーバを見つけ、最終的には そのDNSサーバに対してwwwというホスト名の検索を依頼する その結果が最初にDNSの検索要求を出したマシンに伝えられ、名前解決 が完了する * IPAddress は通信をするすべての Program に使われてます
___

■ IPAddress.と.MacAddress.の対応

DESC internet では IP Address が付加した data が行き来している MacAddress が最終的な Address ( IP Address を変換する必要あり ) ARP ( AdressResoulutionProtocol )が解決してくれます arp コマンドで IP < -> MacAddress の変換表がみる。
  // > arp -p

  Interface: 192.168.0.2 --- 0x2
    Internet Address      Physical Address      Type
    192.168.0.1           xx-xx-xx-xx-xx-xx     dynamic   

___

■ Web

DESC WWW. Mail は Internet の利用したサービスひとつ。 HyperTextTransferProtocol (HTTP) 「http://」というのは「通信方式はHTTPを使いますよ」という意味 webを閲覧するときには HyperTextTransferProtocol というプロトコルを利用して通信する 実は「http://」というのは「通信方式はHTTPを使いますよ」 と説明を意味する。 実は新聞や雑誌の広告に度々登場する「http://」というのは 「インターネットを使って通信を行う時のプロトコルはHTTPを使ってくださいね」 という意味 他にこんな通信方法( Protocol )がある。
     http://              
     ftp://
     https://
     gopher://
HTTP の基礎的な部分はとても単純でわかりやすいプロトコル( 通信方法 )です クライアントである web ブラウザ はwebサーバに TCP でコネクションを張る。 出来上がった TCPコネクションを通じて「このページ下さい」 というとサーバは「ハイどうぞ」とページを渡してくれる。 HTTP の動作
  [ Browser ] < -------------------> [ Web Server ] tomcat は WebServer なのか ? 
     |  ( このページください ) ---> 
     |  
     |  
     |  < --- ( はい, どーぞ ) 
     |  
ブラウザにURLをいれると、こんなことおきてます

    [ Browser ]

        // URL を打ちます
        // これは http 通信方式で送るんだな!
        // で送り先は xxx.xxxx.jp だな!

        // でもこのままだと送れないから, DNS に教えてもらおう
        // 
        [http://xxx.xxxx.jp/xxx.htm]

ブラウザはIPアドレスに向けて [ TCP コネクションを確立 ]しようとする。 TCPコネクションを確立するには、TCPヘッダを含むIPパケットを webサーバへ送信する。 IPパケットを送信するためにブラウザが動作している機器のOS は手元にある経路情報を確認して送信するネットワークインターフェースを調べます 調べた結果、機器に付属しているイーサネット経由でTCPパケットを送信すれば webサーバに送れそうだという事がわかったとします。 送信するネットワークインターフェースがわかると ブラウザが動作している機器は TCPパケットをネットワークインターフェースに送信します ブラウザが動作している機器から送信されたTCPパケットは イーサネット経由でルータに届きます ルータは、TCPパケットのIPヘッダを見て転送する先を考えます ルータのバケツリレー
    転送先は、ルータがもつ経路表を見て決まる。
    ルータは次のルータに TCPパケットを転送する。
    あとはこの繰り返し

    [ RouterA ] ----> [ RouterC ] ------> ...

          経路表
          [---------]
          [---------]
          [---------]
          [---------]
          [---------]
          [---------]

最終的にTCPパケットはwebサーバまで転送される。 webサーバは、転送されてきたTCPパケットを受け取ると ブラウザがある機器に対して返事をする。 (webサーバが返事をできるのは TCPパケットに送信元IPアドレスが記述されているためです。) このようにしてブラウザの機器とwebサーバの間にTCPコネクションが確立します。 Header は 郵便物のあて先. ブラウザは、作成したTCPコネクションを使って -> ここまでは, 単なる[ コネクション処理 ] HTTP プロトコルで www.yahoo.co.jp 内の「/index.html」というページを要求します web サーバは、それに応答してページデータを送信する。 送信されたデータの記述を見てブラウザはページを表示する。 (データの記述にはHTML(HyperTextMarkupLanguage)という言語が使われます 但し、画像やその他言語によるデータがHTTP上に流れたりもします。)






金利比較.COM
NINJAIDX 13