並行システム システム情報系情報工学域, システム情報工学研究科コンピュータサイエンス専攻 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22
あるいは、次のページから手繰っていくこともできます。
http://www.cs.tsukuba.ac.jp/~yas/cs/
http://www.cs.tsukuba.ac.jp/~yas/
ネットワークで接続された複数のコンピュータ(networked mulitple computers)上で複数のプロセス(multiple processes)を動作させる。プロセス は、全体として1つの仕事(a single task)を成し遂げる。
ノード(node):プロセス(process)、あるいは、コンピュータ(computer)
メッセージ(message):ノード間で交換されるデータ data exchanged among nodes
分散システムでは、次のような集中システムでは普通に使える共有資源(no shared )が使えない。
信頼性性があるかないか。専門用語では、程度問題(高い低い)ではなく、有 るか無いか。
信頼性がある通信プリミティブを利用した場合、あるプロセスが送信したメッ セージは、途中で失われることはなく(without message loss)、有限時間以内 (within a finite time)に通信相手のプロセスに送り届けられる。
分散システムでは、メッセージは、失われることがある。 a message can be lost.
図? 結合が作られる通信プリミティブ/sending and recieving messagess with connection-oriented communication primitives
図? 結合が作られる通信プリミティブと結合が作られない通信プリミティブ/sending and recieving messagess with connectionless communication primitives
着信側 callee
make_port(); // 受付端の登録。 accept(); // 実際の受付。connect() と対応。 receive(); send(); receive(); send(); close(); // 接続の切断。発信側 caller
connect(); // 接続要求。accept() と対応。 send(); receive(); send(); receive(); close(); // 接続の切断。
プロセスA
receive(); send(); receive(); send();プロセスB
send(); receive(); send(); receive();
ほとんどの通信プリミティブは、間接。 Most communication primitives use indirect addressing.
直接の例: few examples of direct addressing
問題:直接アドレス指定では、receiveする前にsendされると、どのプロセス にメッセージを送っていいのかわからない。
解決
図? 同期型send/synchronous send() 図? 非同期型send/asynchronous send()
send,receive それぞれ2種類ある。
非同期の方が、並行性が高い。しかし、弱点も多い。 asynchronous communication has higher concurrency, but has many drawbacks.
2. は、転送完了割り込み(interrupt)で解決可能。プログラミングが難しい。 割り込みよりは、マルチスレッドがよい。
プログラミングスタイルの選択 programming styles
同期、非同期とは直行しているとも言えるが、普通は同期の場合にはバッファ リングを省略してコピーを減らす。
メールボックス(mailbox)には、しばしば有限のバッファ(bounded buffer)がも うけられる。
バッファがフルだった時にどうするか。 フルの時にだけ送信側をブロックさせる、という方法でいいのか。 In asyncronous primitives, can we block when a buffer is full?
普通の固定電話(fixed-line phone)の場合、64k bps の帯域予約(bandwidth reservation)、帯域保証(bandwidth guarantee) がなされている。電話を掛け る人が増えてきたとしても、それが 32k bps に減らされるようなことはない。 その代りに、電話を掛けようとすると「通話中(busy)」というエラーが返り、 通信そのものが行えなくなる。
TCP | IP | UDP | イーサネット | 電話 | 郵便 | |
send | 非同期 | 非同期 | 非同期 | 非同期 | 非同期 | 非同期 |
receive | 同期 | (非同期)* | 同期 | (非同期)* | 同期 | 非同期 |
信頼性 | あり | なし | なし | なし | あり | なし |
アドレス指定 | 間接 | 間接 | 間接 | 間接 | 間接 | 直接 |
結合 | あり | なし | なし | なし | あり | なし |
方向 | 双方向 | 単方向 | 単方向 | 単方向 | 双方向 | 単方向 |
マルチキャスト | 不可 | 可能 | 可能 | 可能 | 可能 | 不可 |
帯域保証 | なし | なし | なし | なし | あり | なし |
今後 TCP/IP 以外にも様々な通信プロトコルが開発され、Unix で利用できる ように設計されている。TCP/IP で使う時には、煩雑である。
s = socket(domain, type, 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) |
s = socket(PF_INET,SOCK_STREAM, 0) // TCP, ok. s = socket(PF_INET,SOCK_SEQPACKET,0) // error. s = socket(PF_NS, SOCK_SEQPACKET,0) // XNS sequenced packets, ok.
名前(name) | 説明(description) |
socket() | 通信プロトコルに対応したソケット・オブジェクトを作成する |
connect() | 結合(conection)を確立させる。サーバのアドレスを固定する。 |
listen() | サーバ側で接続要求の待ち受けを開始する。 |
accept() | サーバ側で接続されたソケットを得る。 |
bind() | ソケットにアドレス(名前)を付ける。 |
getpeername() | 通信相手のアドレス(名前)を得る。 |
getsockname() | 自分のアドレス(名前)を得る。 |
send(), sendto(), sendmsg() | メッセージを送信する。 |
recv(), recvfrom(), recvmsg() | メッセージを受信する。 |
shutdown() | 双方向の結合を部分的に切断する。 |
getsockopt() | オプションの現在の値を取得する。 |
setsockopt() | オプションを設定する。 |
select(), poll() | 複数の入出力(通信を含む)を多重化する。 |
write() | メッセージを送信する。 |
read() | メッセージを受信する。 |
close() | ファイル記述子を閉じる。他に参照しているファイル記述子がなければ、ソケット・オブジェクトを削除する。 |
名前(name) | 説明(description) |
gethostbyname() | ホスト名から IP アドレスを調べる。 |
getaddrinfo() | ホスト名から IP アドレスを調べる。IPv6対応。 |
gethostbyaddr() | IPアドレスからホスト名を調べる。 |
getnameinfo() | IPアドレスからホスト名を調べる。IPv6対応。 |
freeaddrinfo() | getaddrinfo(), getnameinfo() で得られた構造体を解放する。 |
tcp_acc_port( int portno )
(サーバ側)
int tcp_connect( char *server, int portno )
(クライアント側)
accept()
をそのまま使う。
accept()
は、1つのクライアントから接続要求を受け付ける。
第1引数の socket は、tcp_acc_port() で作成したソケットを渡す。
TCP/IP では、クライアント側とサーバ側でソケット・オブジェクトの作成する クラスが違っている。
クラス名(class name) | 説明 (description) |
---|---|
Socket | TCP/IP のクライアント側のソケット |
ServerSocket | TCP/IP のサーバ側のソケット |
DatagramSocket | UDP/IP のソケット |
以後、ネットワークか ら文字列を入力するには、InputStreamReader や BufferedReader のオブジェ クトを生成して利用する。
出力側では、Socket クラスのオブジェクトに対して getOutputStream() して、 OutputStream クラスのオブジェクトを得て、 PrintStream オブジェクトを生成して利用できる。
図? marshalingとunmarshaling
4個の要素からなる構造体(a structure with four components)を整列化して送信している。ネットワーク上を流れている時には、整列化されたデータの先頭にはネットワー クのヘッダ(network headers)が付加されている。
C言語で扱える整数 Integers in a C languages
2バイト以上の整数をメモリに保存する方法: メモリの下位番地 に上位バイト(most significant byte)を置くか下位バイトを置くか
main() { int *p; p = (int *)0x2000; *p = 0x12345678; }
.globl _main .align 4, 0x90 _main: pushq %rbp movq %rsp, %rbp movl $305419896, 8192 popq %rbp ret
図? バイト・オーダ(byte order)
名前 Name | 方向 direction | ビット数 # of bits |
uint32_t htonl(uint32_t hostlong) | ホストからネットワークへ変換 convert host to network byte order | 32ビット |
uint16_t htons(uint16_t hostshort) | ホストからネットワークへ変換 convert host to network byte order | 16ビット |
uint32_t ntohl(uint32_t netlong) | ネットワークからホストへ変換 convert network to host byte order | 32ビット |
uint16_t ntohs(uint16_t netshort) | ネットワークからホストへ変換 convert network to host byte order | 16ビット |
unsigned long int hostlong, netlong; hostlong = 0x12345678 ; netlong = htonl( hostlong ); send(conn, &netlong, sizeof(netlong), 0);
送信側(sender):
char buf[BUFSIZE]; hostlong = 0x12345678 ; snprintf(buf,BUFSIZE,"%d\n",hostlong ); send(conn, buf, strlen(buf), 0);思ったほど遅くはない。
注意:sscanf() は、整数をデコードするために使う分には問題ないが、文字列 を受け取るために使うとバッファ・オーバーフロー(buffer overflow)が生じる 可能性があるので、使わない方がよい。
rpcgen というスタブ・コンパイラがある。 データ構造を与えると、marshaling を行う手続きを自動生成する。
SunRPC を使わなくて、XDR だけを使う方法もある。
通信を構造化(structuring of interprocess communication)。 send()/receive() を直接使うのは、goto (jump) でプログラムを書くようなも の。call/if/while で書きたい。
プロセスを2種類に分類する。通信は、次のパタンを繰り返す。 Classify processes into two types.
図? 通信のパタンからみたクライアントとサーバの定義
図? 構造化されていないもの(unstructured communication)
図? 構造化されたもの(structured communication)
構造化プログラミング(structured programming):制御構造(control flow)で 分かりにくいgoto文をつかわないで、わかりやすいgoto文だけ使う。
図? サービスの授受によるクライアントとサーバの定義/definition of client and server by service providing and receiving
図? 複数のクライアントによるサーバの共有/sharing of a server by multiple clients
以上のように、クライアントとサーバは、いろいろな意味で使われる。これら の意味は、多くの場合、一致しているが、一致していないこともある。
通信を開始するパタンで、コンピュータ、プログラム、人間は、次の2つに分 類される。
図? 能動的なクライアントと受動的なサーバ/active clients and a passive server
例:Webサーバは、WWWクライアントから何か要求が来ない限り、ずっと 黙っている。
コンピュータを使う時には、人間が能動的になり、コンピュータが受動的にな る。
テレビを見ている時には、人間が受動的になり、テレビが能動的になる。
講義形式の授業では、サービスの授受では、教官がサーバで、学生がクライア ントになる。通信の開始の方法では、教官が能動的になり、学生が受動的にな る。
大学以上では、学生は、能動的になることが求められている。
混沌とした通信を 構造化(structuring) してわかりやすくしたものが、クライアント・サーバ・モデル(a client server model)である。
サーバあるシステムでは、サーバが落ちるとシステム全体が動作しなくなる。 このように複数の要素から構成されているシステムで、ある要素が故障した時 に、全体が動作しなくなるような場所を、単一障害個所(single point of failure) という。
コンピュータサイエンスでは、古くから、単一障害個所を避けるための研究が 行われてきている。もっとも成功している方法は、サーバを複数(multiple servers)用意する方法である。
サーバがないシステムでは、下手に作るとどの要素が故障してもシステム全体 が止まってしまうことになる。
サーバがないシステムで成功している例はある。
peer は、「対等の仲間」の意味。「通信相手」という意味もある。
サーバがない方法の利点(特徴)
図? ネットワークOSでのアプリケーションの実行
次の2つの機能を、クライアント・サーバ・モデルに基づき、TCP/IP を使って 実装する。$ mkdir hiscore-tcp
$ cd hiscore-tcp
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/add-hiscore.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/highscore-client.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/highscore-server.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/highscore-tcp.h
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/marshaling-burffer.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/marshaling-burffer.h
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/show-hiscore.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2013/2013-10-22/ex/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 "Shuichi Oikawa"
$ ./show-hiscore
Usage: $ ./show-hiscore host portno n
$ ./show-hiscore azalea20 1231 10
2 hiscore record(s) received
20 Shuichi Oikawa
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で用いる要求メッセージの形式 request message format
図? 要求メッセージの例
図? 応答メッセージの例
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_*/
score_record_t
で表現する。
この構造体は、全体で32バイトある。の最初の4バイトは、整数型で、得点、残
りの28バイトは、文字の配列。ASCII で最大27文字まで保存できる。文字が含
まれない部分は、0が入る。
score_record_t
型の配列で表す。
get_highscore_client()
と
put_score_client
がある。
図?モジュールの構成
int marbuf_init( marbuf_t *mb, size_t len )
void marbuf_final( marbuf_t *mb )
int marbuf_send_message( marbuf_t *mb, int socket )
int marbuf_receive_message( marbuf_t *mb, int socket )
int marbuf_marshal_int( marbuf_t *mb, int data )
int marbuf_unmarshal_int( marbuf_t *mb, int *datap )
int marbuf_marshal_byte_array( marbuf_t *mb, char data[], int data_len )
int marbuf_unmarshal_byte_array( marbuf_t *mb, char data[], int data_len )
marbuf_t message; long int x; marbuf_init( &message, MAX_MESSAGE_SIZE ); marbuf_marshal_int( &message, x ); marbuf_send_message( &message, socket ); marbuf_final( &message, socket );基本的な使い方(usage): メッセージの受信とアンマーシャリング receiving and unmarshaling a message
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 );
put_score_client()
関数を呼び出す簡単なプログラム。
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: }
put_score_client()
関数を呼び出す。
get_hiscore_client()
関数を呼び出す簡単なプログラム。
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: }
put_score_client()
関数を呼び出す。
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, size_t addrlen, 21: char *hostname, int portno ); 22:
tcp_connect()
と sockaddr_in_init()
を使う。
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:
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: }
115: /* From Coins System Program */ 116: int tcp_connect( char *server, int portno ) { ... 136: int sockaddr_in_init( struct sockaddr_in *addr, size_t addrlen, 137: char *hostname, int portno ) { ...
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:
tcp_acc_port()
, tcp_peeraddr_print()
,
sockaddr_print()
を使う。
hiscore_records[]
にハイスコアのデータを保持する。
その要素数は、hiscore_nelements
に保持する。
main()
は、文字列でポート番号を引数にとり、それを整数にして
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:
tcp_acc_port()
で、接続受付の準備をする。
accept()
で、1つのクライアントから接続を受け付ける。
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:
marbuf_receive_message()
で、要求メッセージを受信する。
cmd
にコマンドに(整数型)を1つ取出す。
cmd
が PUT_SCORE の場合、
cmd
が GET_HISCORE の場合、
hiscore_records[]
のi番目の要素を応答メッ
セージに加える。
hiscore_records[]
の
i番目の要素のスコアを、応答メッセージに加える。
hiscore_records[]
の
i番目の要素の名前をを、応答メッセージに加える。
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 ) { ...
print_my_host_port()
は、自分自身のホスト名と通信ポート番号を表示する。
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 ) { ...
... 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; ...
... 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]
次の内容を含む「テキスト」ファイルを作成し、 レポート提出ページ から提出しなさい。
回答は、講義内容にそったものにしなさい。信頼できないソースから得た情報 を回答しないこと。
New report submissions due: October 28, 2013, 23:59:59.
The code using htonl() is a part of a program that sends a 32-bit (4-byte) integer to a network after considering the byte order issue. The following code is a part of the program that receives an integer. Fill in the field "/*(A)*/" to "/*(D)*/" and complete the program. Note that "conn" is a file descriptor of a socket of TCP/IP, and receive() is the system call to receive a message.
unsigned long int hostlong, netlong; receive(conn, /*(A)*/, /*(B)*/, 0); /*(C)*/ = /*(D)*/ ; printf("%lu\n",hostlong ); // show the recieved data.
クライアント・サーバ・モデルに基づき分散システムのプログラムを記述する ことを考える。この時、単に Socket API を使う方法と比較して、遠隔手続き 呼び出しを使う方法の利点をこの授業のページから1つ上げなさい。そして数 行で簡単に説明しなさい。
Consider writing a program of a distributed system. Choose an advantage of using a client server model from this page over using simple plain Socket API. Describe this advantage in a few sentences.
From the program highscore-server.c, extract the lines that perform unmarshaling a request message from a client with put_hiscore_client(). Fill in the field "/*(A)*/" to "/*(H)*/" of the following code. Note that "if" and "switch" statements are omitted in the following code.
marbuf_init(/*(A)*/ ); marbuf_unmarshal_/*(B)*/( /*(C)*/ ); marbuf_unmarshal_/*(D)*/( /*(E)*/ ); marbuf_unmarshal_/*(F)*/( /*(G)*/ ); marbuf_final( /*(H)*/ );