通信プリミティブとクライアント・サーバ・モデル(2)

情報学類 分散システム					2008年12月16日

                                       筑波大学システム情報工学研究科
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/dsys-2008/2008-12-16
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■復習

プログラムのパタン。

◆クライアント

要求を一度だけ送る場合。クライアントは複数回動作したり、複数のクライア ントが同時に動くことが多い。
main() {
    connect(); // 結合が作られる通信プリミティブを使う時
    marshal();
    send();
    receive();
    unmarshal();
    close();   // 結合が作られる通信プリミティブを使う時
}

◆サーバ

サーバ側は、無限ループを含み、何度でも要求を受け取る。
main()
  make_port(); // 結合が作られる通信プリミティブを使う時
  while( 1 )
  {
    accept(); // 結合が作られる通信プリミティブを使う時
    receive();
    unmarshal();
    仕事();
    marshal();
    send();
    close(); // 結合が作られる通信プリミティブを使う時
 }
}
marshaling のことを Java 用語では、serialize という。

■今日の大事な話

ゲームのハイスコア(得点の高い人n人の得点と名前)の維持。 集中アプリケーション(12月2日)ネットワーク・オペレーティング・システム で動作させる。

■Socket API

ソケットAPIは、TCP/IP をBSD 系 Unix に導入する時に設計された API であ る。

今後 TCP/IP 以外にも様々な通信プロトコルが開発され、Unix で利用できる ように設計されている。TCP/IP で使う時には、煩雑である。

◆ソケットAPIでのプロトコルの指定

    int socket = socket(int domain, int type, int protocol)
主に domain と type で利用するプロトコルを指定する。 最後の引数 protocol は、普段は 0 を指定する。
ドメイン(domain) 型(type) プロトコル(protocol)
PF_INET SOCK_STREAM TCP(IPv4)
PF_INET SOCK_DGRAM UDP(IPv4)
PF_INET6 SOCK_STREAM TCP(IPv6)
PF_INET6 SOCK_DGRAM UDP(IPv6)
PF_UNIX SOCK_STREAM 同一ホスト内(UNIXドメイン)のストリーム
PF_UNIX SOCK_DGRAM 同一ホスト内(UNIXドメイン)のデータグラム
PF_NS SOCK_STREAM XNS のストリーム(SPP)
PF_NS SOCK_SEQPACKET XNS の順序付きパケット(IDP)
PF_NS SOCK_RDM XNSの信頼性のあるデータグラム(SPP)

◆ソケットAPIの主要なシステムコール、または、ライブラリ関数

名前 説明
socket() 通信プロトコルに対応したソケット・オブジェクトを作成する
connect() 結合(conection)を確立させる。サーバのアドレスを固定する。
listen() サーバ側で接続要求の待ち受けを開始する。
accept() サーバ側で接続されたソケットを得る。
bind() ソケットにアドレス(名前)を付ける。
getpeername() 通信相手のアドレス(名前)を得る。
getsockname() 自分のアドレス(名前)を得る。
send(), sendto(), sendmsg() メッセージを送信する。
recv(), recvfrom(), recvmsg() メッセージを受信する。
shutdown() 双方向の結合を部分的に切断する。
getsockopt() オプションの現在の値を取得する。
setsockopt() オプションを設定する。
select(), poll() 複数の入出力(通信を含む)を多重化する。
write() メッセージを送信する。
read() メッセージを受信する。
close() ファイル記述子を閉じる。他に参照しているファイル記述子がなければ、ソケット・オブジェクトを削除する。
write(), read(), close() はファイルと共通。

◆主要な IP アドレスを扱う関数

名前 説明
gethostbyname() ホスト名から IP アドレスを調べる。
getaddrinfo() ホスト名から IP アドレスを調べる。IPv6対応。
gethostbyaddr() IPアドレスからホスト名を調べる。
getnameinfo() IPアドレスからホスト名を調べる。IPv6対応。
freeaddrinfo() getaddrinfo(), getnameinfo() で得られた構造体を解放する。
関数名には、IP以外も考えた名前にして欲しかった。

◆講義用TCP Librar(講義用のTCP接続を作成するためのライブラリ)

情報学類の講義、 分散システムシステムプログラムで 新城が例題を示すために作成したAPI。 実際の通信は、send(), recv(), write(), read() 等で行う。
tcp_acc_port( int portno ) (サーバ側)
TCP/IP で、サーバ側の接続を受け付けるためのソケットを作る。引数 portno は、サーバ側の TCP のポート番号。これ以降、クライアントは接続要 求を行える。
int tcp_connect( char *server, int portno ) (クライアント側)
サーバ名 server のポート番号 portno に TCP の接続を確立させる。
その他に、サーバ側では、Socket API のaccept() をそのまま使う。 accept() は、1つのクライアントから接続要求を受け付ける。 第1引数の socket は、tcp_acc_port() で作成したソケットを渡す。

◆JavaのAPI

Java言語は、基本的に TCP/IP と UDP/IP しかサポートしていない。したがっ て、TCP/IP や UDP/IP のプログラムを作成する場合には、分かりやすくなっ ている。

TCP/IP では、クラ イアント側とサーバ側でソケット・オブジェクトの作成するクラスが違ってい る。

クラス名 説明
Socket TCP/IP のクライアント側のソケット
ServerSocket TCP/IP のサーバ側のソケット
DatagramSocket UDP/IP のソケット
Java でも、実際の通信には、ファイルと同じ API を用いる。 例: Socket クラスのオブジェクトに対して getInputStream() というメソッドを実行する と、InputStream クラスのオブジェクトが返される。 InputStream は、ファイルからの入力と共通。

以後、ネットワークか ら文字列を入力するには、InputStreamReader や BufferedReader のオブジェ クトを生成して利用する。

出力側では、Socket クラスのオブジェクトに対して getOutputStream() して、 OutputStream クラスのオブジェクトを得て、 PrintStream オブジェクトを生成して利用できる。

■分散アプリケーションの例

ゲームのハイスコア(得点の高い人n人の得点と名前)の維持。

ネットワークOS、ゲーム本体、データ保持プロセス、通信

図? ネットワークOSでのアプリケーションの実行

ライブラリの機能。クライアントとして働く。 サーバは、サーバ server のポート番号 portno で動作している。
get_highscore_client(char *server, int portno, score_record_t records[], int len )
現在のハイスコア(score_record_t型)を最大 len 個だけ取得する。 実際に保存していた数を返す。
int put_score_client(char *server, int portno, int score, char *user)
新たにスコアを追加する。現在のものよりも得点が高ければ追加する。 引数は、得点とユーザ名。
これを、Socket API を提供しているネットワーク・オペレーティング・システム で動作させたい。 集中アプリケーション と対比すこと。

◆実行例

例題をコピーするには、cp コマンドを使うとよい。最後のディレクトリ名 「.」を忘れないように。
% mkdir hiscore-tcp [←]
% cd hiscore-tcp [←]
% cp ~yas/dsys/highscore/tcp/add-hiscore.c . [←]
% cp ~yas/dsys/highscore/tcp/highscore-client.c . [←]
% cp ~yas/dsys/highscore/tcp/highscore-server.c . [←]
% cp ~yas/dsys/highscore/tcp/highscore-tcp.h . [←]
% cp ~yas/dsys/highscore/tcp/marshaling-burffer.c . [←]
% cp ~yas/dsys/highscore/tcp/marshaling-burffer.h . [←]
% cp ~yas/dsys/highscore/tcp/show-hiscore.c . [←]
% cp ~yas/dsys/highscore/tcp/Makefile . [←]
% ls [←]
Makefile		highscore-server.c	marshaling-burffer.h
add-hiscore.c		highscore-tcp.h		show-hiscore.c
highscore-client.c	marshaling-burffer.c
% []
% make [←]
cc -g   -c -o add-hiscore.o add-hiscore.c
cc -g   -c -o highscore-client.o highscore-client.c
cc -g   -c -o marshaling-burffer.o marshaling-burffer.c
cc -g add-hiscore.o highscore-client.o marshaling-burffer.o -o add-hiscore
cc -g   -c -o show-hiscore.o show-hiscore.c
cc -g show-hiscore.o highscore-client.o marshaling-burffer.o -o show-hiscore
cc -g   -c -o highscore-server.o highscore-server.c
cc -g highscore-server.o marshaling-burffer.o -o highscore-server
% ls [←]
Makefile		highscore-server	marshaling-burffer.o
add-hiscore		highscore-server.c	show-hiscore
add-hiscore.c		highscore-server.o	show-hiscore.c
add-hiscore.o		highscore-tcp.h		show-hiscore.o
highscore-client.c	marshaling-burffer.c
highscore-client.o	marshaling-burffer.h
% []
make コマンドを実行すると、Makefile の記述に従い3つの実行 形式のファイル highscore-server, add-hiscore, add-hiscore が作られる。

例題を実行するには、クライアント用とサーバ用に端末を2つ開く。 その方法は、make help で表示される。

% make help [←]
Open two terminals for server and client

server:
./highscore-server portno
(To stop this server, Press ^C)

client:
./add-hiscore server portno score name
./show-hiscore server portno num

% []

クライアントとサーバは、別のコンピュータで動作させても良い。 以下の例では、サーバをazalea20 で動作させている。 サーバ側:

% ssh azalea20 [←]
% cd hiscore-tcp/ [←]
% ./highscore-server  [←]
Usage: % ./highscore-server portno
% ./highscore-server 1231 [←]
run client azalea20 1231 
(To stop this server, Press ^C)
サーバは終了しないので、実験が終わったら ^C で止める。

クライアント側は、もう1つの端末で実行する。

% ./add-hiscore [←]
Usage: % ./add-hiscore server portno score "User Name"
% ./add-hiscore azalea20 1231 10 "Yasushi Shinjo" [←]
% ./add-hiscore azalea20 1231 20 "Kazuhiko Kato" [←]
% ./show-hiscore [←]
Usage: % ./show-hiscore host portno n
% ./show-hiscore azalea20 1231 10 [←]
2 hiscore record(s) received
        20 Kazuhiko Kato
        10 Yasushi Shinjo
% []
実験が終了したら、サーバを ^C コマンドで削除する。
% ./highscore-server 1231 [←]
run client azalea20 1231 
(To stop this server, Press ^C)
[11418] connection (fd==4) from 130.158.86.207:60898
[11418] connection (fd==4) from 130.158.86.207:60900
[11418] connection (fd==4) from 130.158.86.207:60903
^C
% []

◆hiscoreプロトコル

ネットワーク上のデータを形式とタイミングを決める。 クライアント・サーバ・モデルの場合、次の2つを決めればよい。

クライアント、サーバ、要求、応答

図? クライアント・サーバ間で交わされる要求メッセージと応答メッセージ

hiscoreで用いる要求メッセージの形式 hiscoreで用いる応答メッセージの形式

◆メッセージの例

メッセージ長、、コマンド、要素数

図? 要求メッセージの例

メッセージ長、数、レコード

図? 応答メッセージの例

◆hiscore(クライアント)インタフェース

[highscore-tcp.h]

   1:	
   2:	/*
   3:	        highscore-tcp.h -- Hiscore Protocol over TCP
   4:	        Created on: 2008/12/05 19:06:01
   5:	        ~/dsys/highscore/tcp/highscore-proto.h
   6:	*/
   7:	
   8:	#ifndef _HIGHSCORE_TCP_H_
   9:	#define _HIGHSCORE_TCP_H_
  10:	
  11:	#define HISCORE_PROTO_MAX_MESSAGE_SIZE  1024
  12:	
  13:	#define HISCORE_PROTO_GET_HISCORE       1
  14:	#define HISCORE_PROTO_PUT_SCORE         2
  15:	
  16:	#define HISCORE_PROTO_OK                0
  17:	#define HISCORE_PROTO_NO_COMMAND        -1
  18:	#define HISCORE_PROTO_MARSHAL_ERROR     -2
  19:	
  20:	#define HIGHSCORE_MAX_RECORDS   10
  21:	#define HIGHSCORE_NAME_LEN      28
  22:	
  23:	struct score_record {
  24:	        int      score;
  25:	        char     name[HIGHSCORE_NAME_LEN];
  26:	};
  27:	typedef struct score_record score_record_t;
  28:	
  29:	/* for clients */
  30:	extern int get_highscore_client(char *server, int portno,
  31:	                                score_record_t records[], int len );
  32:	extern int put_score_client(char *server, int portno, int score, char *user);
  33:	
  34:	#endif  _HIGHSCORE_TCP_H_

◆モジュールの構成

Socket API,TCP Library,marshaling buffer,hiscore client,hiscore server,client applications

図?モジュールの構成

◆マーシャリング・バッファ・ライブラリ

分散システムの講義用に新城が作成したもの。次の関数で操作する。
int marbuf_init( marbuf_t *mb, size_t len )
マーシャリング/アンマーシャリング用のバッファを確保する。バッファ・サイズは、len バイト。 最大 len バイトのメッセージを送受信できる。
void marbuf_final( marbuf_t *mb )
マーシャリング/アンマーシャリング用のバッファを解放する。
int marbuf_receive_message( marbuf_t *mb, int socket )
マーシャリングしたメッセージを socket を使ってネットワークへ送信する。 メッセージの先頭には、メッセージのバイト数を付ける。
int marbuf_send_message( marbuf_t *mb, int socket )
ネットワークから socket を使ってメッセージを受け取り、バッファに保存する。 メッセージの先頭には、メッセージのバイト数が付けられていることを期待している。
int marbuf_marshal_int( marbuf_t *mb, int data )
引数 data で与えられた整数をマーシャリングしてバッファに追加する。
int marbuf_unmarshal_int( marbuf_t *mb, int *datap )
バッファから整数をアンマーシャリングして取出す。結果は、 ポインタ型の引数 datap で指定された場所に保存する。
int marbuf_marshal_byte_array( marbuf_t *mb, char data[], int data_len )
引数 data 番地から data_len バイトのバイト列を(そのまま)バッファ に追加する。バイト列は固定長で、受信側でも長さを事前に知っている必要がある。
int marbuf_unmarshal_byte_array( marbuf_t *mb, char data[], int data_len )
バッファから data_len バイトのバイト列を(そのまま)取出す。 バイト列は固定長で、長さを知っている必要がある。
基本的な使い方: メッセージのマーシャリングと送信
marbuf_t message; int x;
    marbuf_init( &message, MAX_MESSAGE_SIZE );
    marbuf_marshal_int( &message, x );
    marbuf_send_message( &message, socket );
    marbuf_final( &message, socket );
基本的な使い方: メッセージの受信とアンマーシャリング
marbuf_t message; int x;
    marbuf_init( &message, MAX_MESSAGE_SIZE );
    marbuf_receive_message( &message, socket );
    marbuf_unmarshal_int( &message, &x );
    marbuf_final( &message, socket );

◆add-hiscore.c

put_score_client() 関数を呼び出す簡単なプログラム。

[add-hiscore.c]

   1:	
   2:	/*
   3:	        add-hiscore.c -- The main function of put_score_client().
   4:	        Created on: 2008/12/06 17:47:08
   5:	        ~yas/dsys/highscore/tcp/add-hiscore.c
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stderr, fprintf() */
   9:	#include <stdlib.h>     /* strtol() */
  10:	#include "highscore-tcp.h"
  11:	
  12:	void usage( char *comname ) {
  13:	        fprintf(stderr,"Usage: %% %s server portno score \"User Name\"\n", comname);
  14:	        exit( 1 );
  15:	}
  16:	
  17:	main( int argc, char *argv[], char *envp[] ) {
  18:	    int score, portno ;
  19:	    char *name, *server ;
  20:	        if( argc != 5 )
  21:	            usage( argv[0] );
  22:	        server = argv[1];
  23:	        portno = strtol( argv[2], 0, 10);
  24:	        score  = strtol( argv[3], 0, 10);
  25:	        name   = argv[4];
  26:	        put_score_client( server, portno, score, name );
  27:	}

◆show-hiscore.c

get_hiscore_client() 関数を呼び出す簡単なプログラム。

[show-hiscore.c]

   1:	
   2:	/*
   3:	        show-hiscore.c -- The main function for get_highscore_client().
   4:	        Created on: 2008/12/06 19:21:52
   5:	        ~yas/dsys/highscore/centralized/show-hiscore.c
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stderr, fprintf() */
   9:	#include <stdlib.h>     /* strtol() */
  10:	#include "highscore-tcp.h"
  11:	
  12:	static void show_score( char *host, int port, int top );
  13:	
  14:	static void usage( char *comname ) {
  15:	        fprintf(stderr,"Usage: %% %s host portno n\n", comname);
  16:	        exit( 1 );
  17:	}
  18:	
  19:	main( int argc, char *argv[], char *envp[] ) {
  20:	    int top, portno ;
  21:	    char *name, *server ;
  22:	        if( argc != 4 )
  23:	            usage( argv[0] );
  24:	        server = argv[1];
  25:	        portno = strtol( argv[2], 0, 10);
  26:	        top = strtol( argv[3], 0, 10);
  27:	        show_score( server, portno, top );
  28:	}
  29:	
  30:	static void show_score( char *server, int portno, int top ) {
  31:	    score_record_t records[HIGHSCORE_MAX_RECORDS];
  32:	    int n, i ;
  33:	        if( top > HIGHSCORE_MAX_RECORDS ) {
  34:	            fprintf(stderr,"Warning: top too large: %d\n",top);
  35:	            top = HIGHSCORE_MAX_RECORDS;
  36:	        }
  37:	        n = get_highscore_client( server, portno, records, top );
  38:	        if( n >= 0 ) {
  39:	            printf("%d hiscore record(s) received\n", n );
  40:	            for( i=0; i<n; i++ ) {
  41:	                printf("%10d %s\n", records[i].score, records[i].name );
  42:	            }
  43:	        }
  44:	        else {
  45:	            printf("error: %d\n", n );
  46:	        }
  47:	}

◆hiscoreクライアント

[highscore-client.c]

   1:	
   2:	/*
   3:	        highscore-client.c -- The hiscore client using TCP/IP stream.
   4:	        Created on: 2008/12/06 17:03:28
   5:	        ~yas/dsys/highscore/tcp/highscore-client.c
   6:	*/
   7:	
   8:	#include <stdio.h>      /* stderr, fprintf() */
   9:	#include <stdlib.h>     /* strtol() */
  10:	#include <string.h>     /* memcpy() */
  11:	#include <sys/types.h>  /* socket() */
  12:	#include <sys/socket.h> /* socket() */
  13:	#include <netinet/in.h> /* struct sockaddr_in */
  14:	#include <netdb.h>      /* getaddrinfo(), freeaddrinfo(), struct addrinfo */
  15:	#include "highscore-tcp.h"
  16:	#include "marshaling-burffer.h"
  17:	
  18:	/* From Coins System Program */
  19:	extern  int tcp_connect( char *server, int portno );
  20:	extern  int sockaddr_in_init( struct sockaddr_in *addr, int addrlen,
  21:	                              char *hostname, int portno );
  22:	

◆get_highscore_client()

  23:	int get_highscore_client( char *server, int portno,
  24:	                              score_record_t records[], int len ) {
  25:	    int sock, cmd, n, i;
  26:	    marbuf_t request, reply;
  27:	        marbuf_init( &request,HISCORE_PROTO_MAX_MESSAGE_SIZE );
  28:	        marbuf_init( &reply,HISCORE_PROTO_MAX_MESSAGE_SIZE );
  29:	        if( !marbuf_marshal_int( &request, HISCORE_PROTO_GET_HISCORE ) )
  30:	            goto error0;
  31:	        marbuf_marshal_int( &request, len );
  32:	        if( (sock = tcp_connect( server, portno )) < 0 ) {
  33:	            perror("tcp_sonnect");
  34:	            goto error0;
  35:	        }
  36:	        if( marbuf_send_message( &request, sock ) < 0 ) {
  37:	            perror("send");
  38:	            goto error1;
  39:	        }
  40:	        if( marbuf_receive_message( &reply, sock ) < 0 ) {
  41:	            perror("recieve");
  42:	            goto error1;
  43:	        }
  44:	        if( !marbuf_unmarshal_int( &reply, &n ) ) {
  45:	            fprintf(stderr,"unmarshal n\n");
  46:	            goto error1;
  47:	        }
  48:	        if( n > len ) {
  49:	            fprintf(stderr,"received message too large: %d > %d\n", n, len );
  50:	            goto error1;
  51:	        }
  52:	        for( i=0 ; i<n; i++ ) {
  53:	            if( !marbuf_unmarshal_int(&reply,&records[i].score ) ) {
  54:	                fprintf(stderr,"unmarshal name\n");
  55:	                goto error1;
  56:	            }
  57:	            if( !marbuf_unmarshal_byte_array(&reply,records[i].name,
  58:	                                             HIGHSCORE_NAME_LEN) ) {
  59:	                fprintf(stderr,"unmarshal name\n");
  60:	                goto error1;
  61:	            }
  62:	        }
  63:	        close( sock );
  64:	        marbuf_final( &request );
  65:	        marbuf_final( &reply );
  66:	        return( n );
  67:	
  68:	error1: close( sock );
  69:	error0: marbuf_final( &request );
  70:	        marbuf_final( &reply );
  71:	        return( -1 );
  72:	}
  73:	

◆put_score_client()

  74:	int put_score_client(char *server, int portno, int score, char *name) {
  75:	    int sock, cmd, ok;
  76:	    marbuf_t request, reply;
  77:	    char name_buf[HIGHSCORE_NAME_LEN];
  78:	        marbuf_init( &request,HISCORE_PROTO_MAX_MESSAGE_SIZE );
  79:	        marbuf_init( &reply,HISCORE_PROTO_MAX_MESSAGE_SIZE );
  80:	        if( !marbuf_marshal_int( &request, HISCORE_PROTO_PUT_SCORE) )
  81:	            goto error0;
  82:	        if( !marbuf_marshal_int( &request, score ) )
  83:	            goto error0;
  84:	        memset( name_buf, 0, HIGHSCORE_NAME_LEN );
  85:	        snprintf( name_buf, HIGHSCORE_NAME_LEN, "%s", name );
  86:	        if( !marbuf_marshal_byte_array( &request, name_buf, HIGHSCORE_NAME_LEN ) )
  87:	            goto error0;
  88:	        if( (sock = tcp_connect( server, portno )) < 0 ) {
  89:	            perror("tcp_sonnect");
  90:	            goto error0;
  91:	        }
  92:	        if( marbuf_send_message( &request, sock ) < 0 ) {
  93:	            perror("send");
  94:	            goto error1;
  95:	        }
  96:	        if( marbuf_receive_message( &reply, sock ) < 0 ) {
  97:	            perror("recieve");
  98:	            goto error1;
  99:	        }
 100:	        if( !marbuf_unmarshal_int( &reply, &ok ) ) {
 101:	            fprintf(stderr,"unmarshal n\n");
 102:	            goto error1;
 103:	        }
 104:	        close( sock );
 105:	        marbuf_final( &request );
 106:	        marbuf_final( &reply );
 107:	        return( ok );
 108:	
 109:	error1: close( sock );
 110:	error0: marbuf_final( &request );
 111:	        marbuf_final( &reply );
 112:	        return( -1 );
 113:	}
 114:	

◆クライアントが利用する講義用TCP Library

 115:	/* From Coins System Program */
 116:	int tcp_connect( char *server, int portno ) {
...
 136:	int sockaddr_in_init( struct sockaddr_in *addr, int addrlen,
 137:	                  char *hostname, int portno ) {
...
  • 講義用TCP Library の一部。

    ◆hiscoreサーバ

    メモリ中にハイスコアのデータを保持する。 (ファイルには保存しないので、終了するとデータは失われる。集中のプログ ラムと同様にファイルに保存するようにすることが一般的。ただし、ファイル に保存する場合には、読み書きするはのサーバだけなので、ロックは不要。)

    [highscore-server.c]

       1:	
       2:	/*
       3:	        highscore-server.c -- The hiscore server using TCP/IP stream.
       4:	        Created on: 2008/12/05 18:56:11
       5:	        ~yas/dsys/highscore/tcp/highscore-server.c
       6:	*/
       7:	
       8:	#include <stdio.h>      /* stderr, fprintf() */
       9:	#include <stdlib.h>     /* strtol() */
      10:	#include <string.h>     /* memcpy() */
      11:	#include <sys/types.h>  /* socket() */
      12:	#include <sys/socket.h> /* socket() */
      13:	#include <netinet/in.h> /* struct sockaddr_in, INADDR_ANY */
      14:	#include <netdb.h>      /* getnameinfo() */
      15:	#include "highscore-tcp.h"
      16:	#include "marshaling-burffer.h"
      17:	
      18:	/* From Coins System Program */
      19:	extern int tcp_acc_port( int portno );
      20:	extern void tcp_peeraddr_print( int com );
      21:	extern void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len );
      22:	extern int tcp_acc_port( int portno );
      23:	
      24:	/* Hiscore data in memory */
      25:	static score_record_t hiscore_records[HIGHSCORE_MAX_RECORDS];
      26:	static int            hiscore_nelements;
      27:	
      28:	static void hiscore_server( int portno );
      29:	static void hiscore_request_reply( int com );
      30:	static void print_my_host_port( int portno );
      31:	static int insert_score( score_record_t records[], int len, int nelement,
      32:	                         int score, char *user );
      33:	static int find_posision( score_record_t records[], int len, int nelement,
      34:	                          int score );
      35:	
      36:	static void usage( char *comname ) {
      37:	        fprintf(stderr,"Usage: %% %s portno\n", comname);
      38:	        exit( 1 );
      39:	}
      40:	
      41:	main( int argc, char *argv[], char *envp[] ) {
      42:	    int portno;
      43:	        if( argc != 2 )
      44:	            usage( argv[0] );
      45:	        portno = strtol( argv[1], 0, 10 );
      46:	        hiscore_server( portno );
      47:	}
      48:	
    

    ◆hiscore_server()

      49:	static void hiscore_server( int portno ) {
      50:	    int acc,com ;
      51:	        acc = tcp_acc_port( portno );
      52:	        if( acc<0 )
      53:	            exit( -1 );
      54:	        print_my_host_port( portno );
      55:	        printf("(To stop this server, Press ^C)\n");
      56:	        while( 1 ) {
      57:	            if( (com = accept( acc,0,0 )) < 0 ) {
      58:	                perror("accept");
      59:	                exit( -1 );
      60:	            }
      61:	            tcp_peeraddr_print( com );
      62:	            hiscore_request_reply( com );
      63:	        }
      64:	}
      65:	
    

    ◆hiscore_request_reply()

      66:	static void hiscore_request_reply( int com ) {
      67:	    marbuf_t request, reply;
      68:	    int cmd;
      69:	        marbuf_init( &request,HISCORE_PROTO_MAX_MESSAGE_SIZE );
      70:	        marbuf_init( &reply,HISCORE_PROTO_MAX_MESSAGE_SIZE );
      71:	        if( marbuf_receive_message( &request, com ) < 0 ) {
      72:	            perror("read");
      73:	            goto error0;
      74:	        }
      75:	        
      76:	        if( !marbuf_unmarshal_int( &request, &cmd ) ) {
      77:	            perror("request_msg cmd");
      78:	            goto error0;
      79:	        }
      80:	        switch( cmd ) {
      81:	        case HISCORE_PROTO_PUT_SCORE:
      82:	        {
      83:	            int score ; char name[HIGHSCORE_NAME_LEN];
      84:	            if( !marbuf_unmarshal_int( &request, &score ) )
      85:	                goto error1;
      86:	            if( !marbuf_unmarshal_byte_array( &request, name, HIGHSCORE_NAME_LEN ) )
      87:	                goto error1;
      88:	            hiscore_nelements = insert_score( hiscore_records, HIGHSCORE_MAX_RECORDS,
      89:	                                              hiscore_nelements, score, name );
      90:	            if( !marbuf_marshal_int( &reply, HISCORE_PROTO_OK ) )
      91:	                goto error1;
      92:	            break;
      93:	        }
      94:	        case HISCORE_PROTO_GET_HISCORE:
      95:	        {
      96:	            int len,i,n ; 
      97:	            if( !marbuf_unmarshal_int( &request, &len ) )
      98:	                goto error1;
      99:	            if( len > HIGHSCORE_MAX_RECORDS )
     100:	                len = HIGHSCORE_MAX_RECORDS;
     101:	            n = len < hiscore_nelements ? len : hiscore_nelements ;
     102:	            /* return n and hiscore_records[0..n-1] to client. */
     103:	            if( !marbuf_marshal_int( &reply, n ) )
     104:	                goto error1;
     105:	            for( i=0 ; i<n; i++ )
     106:	            {
     107:	                if( !marbuf_marshal_int( &reply,hiscore_records[i].score ) )
     108:	                    goto error1;
     109:	                if( !marbuf_marshal_byte_array(
     110:	                        &reply,hiscore_records[i].name,HIGHSCORE_NAME_LEN ) )
     111:	                    goto error1;
     112:	            }
     113:	             break;
     114:	         }
     115:	        default:
     116:	            marbuf_marshal_int( &reply, HISCORE_PROTO_NO_COMMAND );
     117:	            break;
     118:	        }
     119:	        marbuf_send_message( &reply, com );
     120:	
     121:	error0: marbuf_final( &request );
     122:	        marbuf_final( &reply );
     123:	        close( com );
     124:	        return;
     125:	
     126:	error1: marbuf_marshal_int( &reply, HISCORE_PROTO_MARSHAL_ERROR );
     127:	        marbuf_send_message( &reply, com );
     128:	        goto error0;
     129:	}
     130:	
    

    ◆hiscoreサーバのその他の関数

     131:	static void print_my_host_port( int portno ) {
    ...
     138:	static int insert_score( score_record_t records[], int len, int nelement,
     139:	                         int score, char *user ) {
    ...
     138:	static int insert_score( score_record_t records[], int len, int nelement,
     139:	                         int score, char *user ) {
    ...
    
     167:	/* Coins System Program */
     170:	void tcp_peeraddr_print( int com ) {
    ...
     183:	void sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ) {
    ...
     192:	int tcp_acc_port( int portno ) {
    ...
    

    ◆マーシャリング・バッファ・ライブラリの実装

    [marshaling-burffer.h]

    ...
      13:	struct marbuf {
      14:	        uint32_t  mb_bytes;
      15:	        char     *mb_current;
      16:	        char     *mb_buf;
      17:	        size_t    mb_buflen;
      18:	};
      19:	typedef struct marbuf marbuf_t;
    ...
    

    [marshaling-burffer.c]

    ...
      15:	int marbuf_init( marbuf_t *mb, size_t buflen ) {
      16:	        if( (mb->mb_buf = malloc(buflen)) == NULL ) {
      17:	            return( 0 );
      18:	        }
      19:	        mb->mb_buflen  = buflen;
      20:	        mb->mb_bytes   = 0;
      21:	        mb->mb_current = mb->mb_buf;
      22:	}
    ...
      24:	void marbuf_final( marbuf_t *mb ) {
    ...
      32:	int marbuf_receive_message( marbuf_t *mb, int socket ) {
      33:	    uint32_t msglen, msglen_net ;
    ...
      52:	int marbuf_send_message( marbuf_t *mb, int socket ) {
      53:	    uint32_t msglen, msglen_net ;
      54:	        msglen     = mb->mb_bytes;
      55:	        msglen_net = htonl(msglen);
      56:	        if( write(socket,&msglen_net,sizeof(msglen_net)) != sizeof(msglen_net) ) {
      57:	            perror("write");
      58:	            return( -1 );
      59:	        }
      60:	        if( write(socket,mb->mb_buf,msglen) != msglen ) {
      61:	            perror("read");
      62:	            return( -1 );
      63:	        }
      64:	        mb->mb_bytes   = 0;
      65:	        mb->mb_current = mb->mb_buf;
      66:	        return( msglen );
      67:	}
      68:	
      69:	int marbuf_marshal_int( marbuf_t *mb, int data ) {
      70:	    uint32_t data_net ;
      71:	        if( mb->mb_bytes + sizeof(data_net) > mb->mb_buflen )
      72:	            return( 0 );
      73:	        data_net =  htonl( data );
      74:	        memcpy( mb->mb_current, &data_net, sizeof(data_net) );
      75:	        mb->mb_current += sizeof(data_net);
      76:	        mb->mb_bytes   += sizeof(data_net);
      77:	        return( 1 );
      78:	}
      79:	
      80:	int marbuf_unmarshal_int( marbuf_t *mb, int *datap ) {
      81:	    uint32_t data_net ;
      82:	        if( mb->mb_bytes + sizeof(data_net) > mb->mb_buflen )
      83:	            return( 0 );
      84:	        memcpy( &data_net, mb->mb_current, sizeof(data_net) );
      85:	        *datap =  ntohl( data_net );
      86:	        mb->mb_current += sizeof(data_net);
      87:	        mb->mb_bytes   += sizeof(data_net);
      88:	        return( 1 );
      89:	}
      90:	
      91:	int marbuf_marshal_byte_array( marbuf_t *mb, char data[], int data_len ) {
      92:	        if( mb->mb_bytes + data_len > mb->mb_buflen )
      93:	            return( 0 );
      94:	        memcpy( mb->mb_current, data, data_len );
      95:	        mb->mb_current += data_len;
      96:	        mb->mb_bytes   += data_len;
      97:	        return( 1 );
      98:	}
      99:	
     100:	int marbuf_unmarshal_byte_array( marbuf_t *mb, char data[], int data_len ) {
     101:	        if( mb->mb_bytes + data_len > mb->mb_buflen )
     102:	            return( 0 );
     103:	        memcpy( data, mb->mb_current, data_len );
     104:	        mb->mb_current += data_len;
     105:	        mb->mb_bytes   += data_len;
     106:	        return( 1 );
     107:	}
    

    ◆Makefile

    [Makefile]


    Last updated: 2008/12/15 11:30:22
    Yasushi Shinjo / <yas@is.tsukuba.ac.jp>