並行システム システム情報系/情報工学域, システム情報工学研究群/情報理工学位プログラム システム情報工学研究科/コンピュータサイエンス専攻 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
https://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09
あるいは、次のページから手繰っていくこともできます。
https://www.cs.tsukuba.ac.jp/~yas/cs/
https://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 resource is available.
時刻源(clock source, time source)は、わりと使える。
信頼性があるかないか。専門用語では、程度問題(高い低い)ではなく、有 るか無いか。
信頼性がある通信プリミティブを利用した場合、あるプロセスが送信したメッ セージは、途中で失われることはなく(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()と同期型receive()/synchronous send() and synchronous receive() 図? 非同期型send()と同期型receive()/asynchronous send() and synchronous receive() 図? 非同期型send()と非同期型receive()/asynchronous send() and asynchronous receive()
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 | 同期 | (非同期)* | 同期 | (非同期)* | 同期 | 非同期 |
信頼性 | あり | なし | なし | なし | あり | なし |
アドレス指定 | 間接 | 間接 | 間接 | 間接 | 間接 | 間接, 直接** |
結合 | あり | なし | なし | なし | あり | なし |
方向 | 双方向 | 単方向 | 単方向 | 単方向 | 双方向 | 単方向 |
マルチキャスト | 不可 | 可能 | 可能 | 可能 | 可能 | 不可 |
帯域保証 | なし | なし | なし | なし | あり | なし |
* OS 内。
** 「本人限定受取」を使った時。
今後 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() | 通信プロトコルに対応したソケット・オブジェクトを作成する |
listen() | 結合要求の待ち受けを開始する。(サーバ側) | accept() | 結合要求を待ち、それが届けば結合を確立させ、個別の結合されたソケットを返す。(サーバ側) |
connect() | 結合(conection)を要求し、相手が受け入れれば結合を確立する。(クライアント側) |
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 ip_version )
(サーバ側)
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言語で扱える整数の例(コンパイラによって異なる)。 Examples of integers in the C language (depending on compilers).
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ビット |
uint32_t 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でのアプリケーションの実行
次の3つの機能を、クライアント・サーバ・モデルに基づき、TCP/IP を使って 実装する。$ mkdir counter-tcp
$ cd counter-tcp
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-client.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-getvalue.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-reset.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-server.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-tcp.h
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/counter-up.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/marshaling-burffer.c
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/marshaling-burffer.h
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2025/2025-05-09/ex/Makefile
$ ls
counter-client.c counter-server.c Makefile
counter-getvalue.c counter-tcp.h marshaling-burffer.c
counter-reset.c counter-up.c marshaling-burffer.h
$ make
cc -g -c -o counter-server.o counter-server.c
cc -g -c -o marshaling-burffer.o marshaling-burffer.c
cc -g counter-server.o marshaling-burffer.o -o counter-server
cc -g -c -o counter-up.o counter-up.c
cc -g -c -o counter-client.o counter-client.c
cc -g counter-up.o counter-client.o marshaling-burffer.o -o counter-up
cc -g -c -o counter-getvalue.o counter-getvalue.c
cc -g counter-getvalue.o counter-client.o marshaling-burffer.o -o counter-getvalue
cc -g -c -o counter-reset.o counter-reset.c
cc -g counter-reset.o counter-client.o marshaling-burffer.o -o counter-reset
$ ls
counter-client.c counter-reset counter-server.o Makefile
counter-client.o counter-reset.c counter-tcp.h marshaling-burffer.c
counter-getvalue counter-reset.o counter-up marshaling-burffer.h
counter-getvalue.c counter-server counter-up.c marshaling-burffer.o
counter-getvalue.o counter-server.c counter-up.o
$
make
コマンドを実行すると、Makefile
の記述に従い4つの実行
形式のファイル counter-server
,
counter-up
, counter-getvalue
, counter-reset
が作られる。
例題を実行するには、クライアント用とサーバ用に端末を2つ開く。
その方法は、make help
で表示される。
$ make help
Open two terminals for server and client
server:
./counter-server portno
(To stop this server, Press ^C)
client:
./counter-up server portno
./counter-getvalue server portno
./counter-reset server portno newvalue
$
クライアントとサーバは、別のコンピュータで動作させても良い。 以下の例では、サーバを localhost で動作させている。 サーバ側:
$ ./counter-server 1231
run client sharon 1231
[85601] accepting (fd==4) to [::]:1231
(To stop this server, Press ^C)
サーバは終了しないので、実験が終わったら ^C で止める。
クライアント側は、もう1つの端末で実行する。
$ ./counter-getvalue localhost 1231
counter == 0
$ ./counter-up localhost 1231
$ ./counter-getvalue localhost 1231
counter == 1
$ ./counter-up localhost 1231
$ ./counter-getvalue localhost 1231
counter == 2
$ ./counter-reset localhost 1231 100
$ ./counter-getvalue localhost 1231
counter == 100
$
実験が終了したら、サーバを ^C コマンドで削除する。
$ ./counter-server 1231
run client sharon 1231
[85601] accepting (fd==4) to [::]:1231
(To stop this server, Press ^C)
[85601] connection (fd==5) from [::1]:59272
[85601] connection (fd==5) from [::1]:59274
[85601] connection (fd==5) from [::1]:59275
[85601] connection (fd==5) from [::1]:59276
[85601] connection (fd==5) from [::1]:59277
[85601] connection (fd==5) from [::1]:59278
[85601] connection (fd==5) from [::1]:59279
^C
$
counterプロトコル要求メッセージの例/The request message examples of the counter protocol
図? 要求メッセージの例(up)
図? 要求メッセージの例(getvalue)
図? 要求メッセージの例(reset)
counterプロトコル応答メッセージの例/The reply message examples of the counter protocol
図? 応答メッセージの例(ok)
図? 応答メッセージの例(ok+カウンタ値)
図? 応答メッセージの例(エラー)
1: 2: /* 3: counter-tcp.h -- Counter Protocol over TCP 4: ~/dsys/counter/tcp/counter-tcp.h 5: */ 6: 7: #ifndef _COUNTER_TCP_H_ 8: #define _COUNTER_TCP_H_ 9: 10: #define COUNTER_PROTO_MAX_MESSAGE_SIZE 100 11: 12: #define COUNTER_PROTO_UP 1 13: #define COUNTER_PROTO_GETVALUE 2 14: #define COUNTER_PROTO_RESET 3 15: 16: #define COUNTER_PROTO_OK 0 17: #define COUNTER_PROTO_NO_COMMAND -1 18: #define COUNTER_PROTO_MARSHAL_ERROR -2 19: 20: /* for client library */ 21: extern int counter_client_up( char *server, int portno ); 22: extern int counter_client_getvalue(char *server, int portno, int *valuep ); 23: extern int counter_client_reset( char *server, int portno, int value ); 24: 25: #endif /*_COUNTER_TCP_H_*/
counter_client_up
,
counter_client_getvalue
,
counter_client_reset
がある。
図?モジュールの構成
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 mb; int x; int y; marbuf_init( &mb, MAX_MESSAGE_SIZE ); marbuf_marshal_int( &mb, x ); marbuf_marshal_int( &mb, y ); marbuf_send_message( &mb, socket ); marbuf_final( &mb );基本的な使い方(usage): メッセージの受信とアンマーシャリング receiving and unmarshaling a message
marbuf_t mb; int x; int y; marbuf_init( &mb, MAX_MESSAGE_SIZE ); marbuf_receive_message( &mb, socket ); marbuf_unmarshal_int( &mb, &x ); marbuf_unmarshal_int( &mb, &y ); marbuf_final( &mb );
counter_client_up()
関数を呼び出す簡単なプログラム。
1: 2: /* 3: counter-up.c -- The main function of counter client. 4: */ 5: 6: #include <stdio.h> /* stderr, fprintf() */ 7: #include <stdlib.h> /* strtol() */ 8: #include "counter-tcp.h" 9: 10: void usage( char *comname ) { 11: fprintf(stderr,"Usage: %% %s server portno\n", comname); 12: exit( 1 ); 13: } 14: 15: int main( int argc, char *argv[], char *envp[] ) { 16: int score, portno, stat ; 17: char *name, *server ; 18: if( argc != 3 ) 19: usage( argv[0] ); 20: server = argv[1]; 21: portno = strtol( argv[2], 0, 10); 22: counter_client_up( server, portno ); 23: } 24:
put_score_client()
関数を呼び出す。
counter_client_getvalue()
関数を呼び出す簡単なプログラム。
1: 2: /* 3: counter-getvalue.c -- The main function of counter client. 4: */ 5: 6: #include <stdio.h> /* stderr, fprintf() */ 7: #include <stdlib.h> /* strtol() */ 8: #include "counter-tcp.h" 9: 10: void usage( char *comname ) { 11: fprintf(stderr,"Usage: %% %s server portno\n", comname); 12: exit( 1 ); 13: } 14: 15: int main( int argc, char *argv[], char *envp[] ) { 16: int score, portno, stat, value ; 17: char *name, *server ; 18: if( argc != 3 ) 19: usage( argv[0] ); 20: server = argv[1]; 21: portno = strtol( argv[2], 0, 10); 22: stat = counter_client_getvalue( server, portno, &value ); 23: if( stat ) 24: printf("counter == %d\n", value); 25: } 26:
counter_client_getval()
関数を呼び出す。
counter_client_reset()
関数を呼び出す簡単なプログラム。
1: 2: /* 3: counter-reset.c -- The main function of counter client. 4: */ 5: 6: #include <stdio.h> /* stderr, fprintf() */ 7: #include <stdlib.h> /* strtol() */ 8: #include "counter-tcp.h" 9: 10: void usage( char *comname ) { 11: fprintf(stderr,"Usage: %% %s server portno newvalue\n", comname); 12: exit( 1 ); 13: } 14: 15: int main( int argc, char *argv[], char *envp[] ) { 16: int score, portno, value ; 17: char *name, *server ; 18: if( argc != 4 ) 19: usage( argv[0] ); 20: server = argv[1]; 21: portno = strtol( argv[2], 0, 10); 22: value = strtol( argv[3], 0, 10); 23: counter_client_reset( server, portno, value ); 24: } 25:
counter_client_counter_client_reset()
関数を呼び出す。
1: 2: /* 3: counter-client.c -- The counter client using TCP/IP stream. 4: */ 5: 6: #include <stdio.h> /* stderr, fprintf() */ 7: #include <stdlib.h> /* strtol() */ 8: #include <unistd.h> /* close() */ 9: #include <string.h> /* memcpy() */ 10: #include <sys/types.h> /* socket() */ 11: #include <sys/socket.h> /* socket() */ 12: #include <netinet/in.h> /* struct sockaddr_in */ 13: #include <netdb.h> /* getaddrinfo(), freeaddrinfo(), struct addrinfo */ 14: #include "counter-tcp.h" 15: #include "marshaling-burffer.h" 16: 17: /* From Coins System Program */ 18: extern int tcp_connect( char *server, int portno ); 19:
tcp_connect()
と sockaddr_in_init()
を使う。
20: int counter_client_up( char *server, int portno ) { 21: int sock, cmd, stat; 22: marbuf_t request, reply; 23: marbuf_init( &request,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 24: marbuf_init( &reply,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 25: if( !marbuf_marshal_int( &request, COUNTER_PROTO_UP ) ) 26: goto error0; 27: if( (sock = tcp_connect( server, portno )) < 0 ) { 28: perror("tcp_sonnect"); 29: goto error0; 30: } 31: if( marbuf_send_message( &request, sock ) < 0 ) { 32: perror("send"); 33: goto error1; 34: } 35: if( marbuf_receive_message( &reply, sock ) < 0 ) { 36: perror("recieve"); 37: goto error1; 38: } 39: if( !marbuf_unmarshal_int( &reply, &stat ) ) { 40: fprintf(stderr,"unmarshal stat\n"); 41: goto error1; 42: } 43: close( sock ); 44: marbuf_final( &request ); 45: marbuf_final( &reply ); 46: return( 1 ); 47: 48: error1: close( sock ); 49: error0: marbuf_final( &request ); 50: marbuf_final( &reply ); 51: return( 0 ); 52: }
54: int counter_client_getvalue( char *server, int portno, int *valuep ) { 55: int sock, cmd, stat; 56: marbuf_t request, reply; 57: marbuf_init( &request,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 58: marbuf_init( &reply,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 59: if( !marbuf_marshal_int( &request, COUNTER_PROTO_GETVALUE) ) 60: goto error0; 61: if( (sock = tcp_connect( server, portno )) < 0 ) { 62: perror("tcp_sonnect"); 63: goto error0; 64: } 65: if( marbuf_send_message( &request, sock ) < 0 ) { 66: perror("send"); 67: goto error1; 68: } 69: if( marbuf_receive_message( &reply, sock ) < 0 ) { 70: perror("recieve"); 71: goto error1; 72: } 73: if( !marbuf_unmarshal_int( &reply, &stat ) ) { 74: fprintf(stderr,"unmarshal stat\n"); 75: goto error1; 76: } 77: if( stat != 0 ) { 78: fprintf(stderr,"stat == %d\n", stat ); 79: goto error1; 80: } 81: if( !marbuf_unmarshal_int( &reply, valuep ) ) { 82: fprintf(stderr,"unmarshal value\n"); 83: goto error1; 84: } 85: close( sock ); 86: marbuf_final( &request ); 87: marbuf_final( &reply ); 88: return( 1 ); 89: 90: error1: close( sock ); 91: error0: marbuf_final( &request ); 92: marbuf_final( &reply ); 93: return( 0 ); 94: }
96: int counter_client_reset( char *server, int portno, int value ) { 97: int sock, cmd, stat; 98: marbuf_t request, reply; 99: marbuf_init( &request,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 100: marbuf_init( &reply,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 101: if( !marbuf_marshal_int( &request, COUNTER_PROTO_RESET ) ) 102: goto error0; 103: if( !marbuf_marshal_int( &request, value ) ) 104: goto error0; 105: if( (sock = tcp_connect( server, portno )) < 0 ) { 106: perror("tcp_sonnect"); 107: goto error0; 108: } 109: if( marbuf_send_message( &request, sock ) < 0 ) { 110: perror("send"); 111: goto error1; 112: } 113: if( marbuf_receive_message( &reply, sock ) < 0 ) { 114: perror("recieve"); 115: goto error1; 116: } 117: if( !marbuf_unmarshal_int( &reply, &stat ) ) { 118: fprintf(stderr,"unmarshal stat\n"); 119: goto error1; 120: } 121: if( stat != 0 ) { 122: fprintf(stderr,"stat == %d\n", stat ); 123: goto error1; 124: } 125: close( sock ); 126: marbuf_final( &request ); 127: marbuf_final( &reply ); 128: return( 1 ); 129: 130: error1: close( sock ); 131: error0: marbuf_final( &request ); 132: marbuf_final( &reply ); 133: return( 0 ); 134: }
139: int 140: tcp_connect( char *server, int portno )
1: 2: /* 3: counter-server.c -- The counter server using TCP/IP stream 4: based on the client-server model. 5: */ 6: 7: #include <stdio.h> /* stderr, fprintf() */ 8: #include <stdlib.h> /* strtol() */ 9: #include <unistd.h> /* close() */ 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 "counter-tcp.h" 16: #include "marshaling-burffer.h" 17: 18: /* From Coins System Program */ 19: extern void tcp_sockaddr_print( int com ); 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, int ip_version ); 23: 24: /* Counter data in memory */ 25: static int val; ... 36: int main( int argc, char *argv[], char *envp[] ) { 37: int portno, ip_version; 38: 39: if( !(argc == 2 || argc==3) ) { 40: fprintf(stderr,"Usage: %s portno {ipversion}\n",argv[0] ); 41: exit( 1 ); 42: } 43: portno = strtol( argv[1],0,10 ); 44: if( argc == 3 ) 45: ip_version = strtol( argv[2],0,10 ); 46: else 47: ip_version = 46; /* Both IPv4 and IPv6 by default */ 48: counter_server( portno, ip_version ); 49: }
tcp_acc_port()
, tcp_peeraddr_print()
,
sockaddr_print()
を使う。
main()
は、文字列でポート番号を引数にとり、それを整数に変換する。
また、指定されていれば、IP のバージョンを整数に変換する。
それらを counter_server()
を呼び出す。
51: static void counter_server( int portno, int ip_version ) { 52: int acc,com ; 53: acc = tcp_acc_port( portno, ip_version ); 54: if( acc<0 ) 55: exit( -1 ); 56: print_my_host_port( portno ); 57: tcp_sockaddr_print( acc ); 58: printf("(To stop this server, Press ^C)\n"); 59: while( 1 ) { 60: if( (com = accept( acc,0,0 )) < 0 ) { 61: perror("accept"); 62: exit( -1 ); 63: } 64: tcp_peeraddr_print( com ); 65: counter_receive_request_and_send_reply( com ); 66: } 67: }
tcp_acc_port()
で、結合作成の受付の準備をする。
accept()
で、1つのクライアントからの結合作成要求を受け付ける。
counter_receive_request_and_send_reply()
で、要求の受信と応答の送信を行う。
69: static void counter_receive_request_and_send_reply( int com ) { 70: marbuf_t request, reply; 71: int cmd; 72: marbuf_init( &request,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 73: marbuf_init( &reply,COUNTER_PROTO_MAX_MESSAGE_SIZE ); 74: if( marbuf_receive_message( &request, com ) < 0 ) { 75: perror("read"); 76: goto error0; 77: } 78: if( !marbuf_unmarshal_int( &request, &cmd ) ) { 79: perror("request_msg cmd"); 80: goto error0; 81: } 82: switch( cmd ) { 83: case COUNTER_PROTO_UP: 84: { 85: val ++; 86: if( !marbuf_marshal_int( &reply, COUNTER_PROTO_OK ) ) 87: goto error1; 88: break; 89: } 90: case COUNTER_PROTO_GETVALUE: 91: { 92: if( !marbuf_marshal_int( &reply, COUNTER_PROTO_OK ) ) 93: goto error1; 94: if( !marbuf_marshal_int( &reply, val ) ) 95: goto error1; 96: break; 97: } 98: case COUNTER_PROTO_RESET: 99: { 100: int newvalue; 101: if( !marbuf_unmarshal_int( &request, &newvalue ) ) { 102: perror("request_msg newvalue"); 103: goto error1; 104: } 105: val = newvalue; 106: if( !marbuf_marshal_int( &reply, COUNTER_PROTO_OK ) ) 107: goto error1; 108: break; 109: } 110: default: 111: marbuf_marshal_int( &reply, COUNTER_PROTO_NO_COMMAND ); 112: break; 113: } 114: marbuf_send_message( &reply, com ); 115: 116: error0: marbuf_final( &request ); 117: marbuf_final( &reply ); 118: close( com ); 119: return; 120: 121: error1: marbuf_marshal_int( &reply, COUNTER_PROTO_MARSHAL_ERROR ); 122: marbuf_send_message( &reply, com ); 123: goto error0; 124: }
marbuf_receive_message()
で、要求メッセージを受信する。
cmd
にコマンドに(整数型)を1つ取出す。
cmd
が COUNTER_PROTO_UP の場合、val ++ でカウンタ値を増やし、
応答メッセージに COUNTER_PROTO_OK をセットする。
cmd
が COUNTER_PROTO_GETVALU の場合、
応答メッセージに COUNTER_PROTO_OK と
現在のカウンタ値 val を置く。
cmd
が COUNTER_PROTO_RESET の場合、
要求メッセージから新しいカウンタ値(整数型)を取出し、
それをカウンタ値 val に設定する。
応答メッセージに COUNTER_PROTO_OK をセットする。
126: static void print_my_host_port( int portno ) { ...
print_my_host_port()
は、自分自身のホスト名と通信ポート番号を表示する。
133: /* Coins System Program */ ... 136: void 137: tcp_sockaddr_print( int com ) ... 153: void 154: tcp_peeraddr_print( int com ) ... 170: void 171: sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ) ... 187: int 188: tcp_acc_port( int portno, int ip_version ) ...
... 12: struct marbuf { 13: uint32_t mb_bytes; 14: char *mb_current; 15: char *mb_buf; 16: size_t mb_buflen; 17: }; 18: 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: return( 1 ); 23: } ... 25: void marbuf_final( marbuf_t *mb ) { ... 33: int marbuf_receive_message( marbuf_t *mb, int socket ) { ... 53: int marbuf_send_message( marbuf_t *mb, int socket ) { 54: uint32_t msglen, msglen_net ; 55: msglen = mb->mb_bytes; 56: msglen_net = htonl(msglen); 57: if( write(socket,&msglen_net,sizeof(msglen_net)) != sizeof(msglen_net) ) { 58: perror("write"); 59: return( -1 ); 60: } 61: if( write(socket,mb->mb_buf,msglen) != msglen ) { 62: perror("read"); 63: return( -1 ); 64: } 65: mb->mb_bytes = 0; 66: mb->mb_current = mb->mb_buf; 67: return( msglen ); 68: } 69: 70: int marbuf_marshal_int( marbuf_t *mb, int data ) { 71: uint32_t data_net ; 72: if( mb->mb_bytes + sizeof(data_net) > mb->mb_buflen ) 73: return( 0 ); 74: data_net = htonl( data ); 75: memcpy( mb->mb_current, &data_net, sizeof(data_net) ); 76: mb->mb_current += sizeof(data_net); 77: mb->mb_bytes += sizeof(data_net); 78: return( 1 ); 79: } 80: 81: int marbuf_unmarshal_int( marbuf_t *mb, int *datap ) { 82: uint32_t data_net ; 83: if( mb->mb_bytes + sizeof(data_net) > mb->mb_buflen ) 84: return( 0 ); 85: memcpy( &data_net, mb->mb_current, sizeof(data_net) ); 86: *datap = ntohl( data_net ); 87: mb->mb_current += sizeof(data_net); 88: mb->mb_bytes += sizeof(data_net); 89: return( 1 ); 90: }
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 spaces "/*(A)*/" to "/*(D)*/" and complete the program. Note that "conn" is a file descriptor of a socket of TCP/IP, and recv() is the system call to receive a message.
uint32_t hostlong, netlong; recv(conn, /*(A)*/, /*(B)*/, 0); /*(C)*/ = /*(D)*/ ; printf("%lu\n", hostlong ); // show the recieved data.
図? 要求メッセージの例(reset)
この関数のコードから、要求メッセージをマーシャリングして送信している部分だけを 抽出しなさい。以下の /*(A)*/ から /*(G)*/ の欄を埋めなさい。以下のプロ グラムでは、if 文を全て省略してある。From this function, extract the lines that perform marshaling a request message and sending the message. Fill in the field "/*(A)*/" to "/*(G)*/" of the following code. Note that "if" statements are omitted in the following code.
marbuf_init(/*(A)*/ ); marbuf_/*(B)*/( /*(C)*/ ); marbuf_/*(D)*/( /*(E)*/ ); marbuf_send_message( /*(F)*/ ); marbuf_final( /*(G)*/ );