プロセス間通信、マーシャリング、クライアントサーバモデル/ Interprocess communication,marshaling,client-server model

並行システム

                               システム情報系情報工学域,
			       システム情報工学研究科コンピュータサイエンス専攻
                               新城 靖
                               <yas@cs.tsukuba.ac.jp>

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

■今日の重要な話/important topics

■プロセス間通信/ Interprocess communication

2つの重要なパタン two important patterns プロセス間通信の構造化(structuring of interprocess communication)。 send(), receive() は、goto 文相当。

◆プロセスとメッセージ/process and messages

分散システム(a distributed system): 離れていても心は1つ sharing one heart between two separate lovers

ネットワークで接続された複数のコンピュータ(networked mulitple computers)上で複数のプロセス(multiple processes)を動作させる。プロセス は、全体として1つの仕事(a single task)を成し遂げる。

ノード(node):プロセス(process)、あるいは、コンピュータ(computer)

メッセージ(message):ノード間で交換されるデータ data exchanged among nodes

分散システムでは、次のような集中システムでは普通に使える共有資源(no shared )が使えない。

分散共有メモリ(distributed shared memory)、 分散ファイルシステム(distributed file system)が使える 場合は使う方法も検討してもよい。

◆通信プリミティブ/communication primitives

メッセージを送信したり受信したりするライブラリ関数(library functions)や システム・コール(system calls)を通信プリミティブという。 最終的には、ハードウェア。 分散プログラムを記述する時には通信プリミティブを呼び出した段階まで考え ればよい。以後、プログラミング言語の実行時システムやOSが考える。

◆send() and receive()

通信の基本命令 basic operations
send()
メッセージを送信する
receive()
メッセージを受信する。

◆通信プリミティブの分類/classification

References: Andrew S. Tanenbaum and Robbert Van Renesse. Distributed operating systems. ACM Comput. Surv. Vol.17, No.4, pp.419-470 (December 1985). DOI=10.1145/6041.6074 http://doi.acm.org/10.1145/6041.6074

◆信頼性の有無(reliable or non-reliable)

信頼性性があるかないか。専門用語では、程度問題(高い低い)ではなく、有 るか無いか。

信頼性がある通信プリミティブを利用した場合、あるプロセスが送信したメッ セージは、途中で失われることはなく(without message loss)、有限時間以内 (within a finite time)に通信相手のプロセスに送り届けられる。

分散システムでは、メッセージは、失われることがある。 a message can be lost.

信頼性に対する態度 attitude to reliability

◆結合(コネクション)が作られるか結合が作られないか/connecton oriented or connectionless

結合が必要な通信プリミティブ connection oriented
実際に send や receive でメッセージを送受 信する前に結合を確立させる手順を踏む。電話(telephone)。
結合が作られない通信プリミティブ connectionless
送りたいデータをすぐに send することができる。郵便(physical mail)。

プロセスAとプロセスBの間に結合がつくられる

図? 結合が作られる通信プリミティブ/sending and recieving messagess with connection-oriented communication primitives

プロセスAとプロセスBの間に結合が作られない時

図? 結合が作られる通信プリミティブと結合が作られない通信プリミティブ/sending and recieving messagess with connectionless communication primitives

◆接続の確立と通信パタン/connection establishment and communication pattern

接続を作る通信プリミティブでは、send(), receive() で送受信する前に接続 を確立する必要がある。通信の確立では、 電話(teleophone)と似ていて、発信 側(caller)と着信側(callee)に分かれる。

着信側 callee

    make_port(); // 受付端の登録。
    accept();    // 実際の受付。connect() と対応。

    receive();
    send();
    receive();
    send();

    close();     // 接続の切断。
発信側 caller
    connect();   // 接続要求。accept() と対応。

    send();
    receive();
    send();
    receive();

    close();     // 接続の切断。

◆接続を用いない通信/connectionless communication pattern

接続を用いる操作がない。send() と receive() が対応しているだけ。

プロセスA

    receive();
    send();
    receive();
    send();
プロセスB
    send();
    receive();
    send();
    receive();

◆アドレス指定/addressing

直接 direct
プロセスを指定する。targeting a process
間接 indirect
メール・ボックス。 targeting a mailbox. 複数のプロセスによって共有されることもされない こともある。 a mail box can be shard among processes.

ほとんどの通信プリミティブは、間接。 Most communication primitives use indirect addressing.

直接の例: few examples of direct addressing

問題:直接アドレス指定では、receiveする前にsendされると、どのプロセス にメッセージを送っていいのかわからない。

解決

◆同期型/非同期(synchronous/asynchronous)

send() したプロセスはreceive()されるのを待たずにすぐ再開する。

図? 同期型send/synchronous send()

send() したプロセスがreceive()されるまで止まる。

図? 非同期型send/asynchronous send()

send,receive それぞれ2種類ある。

非同期の方が、並行性が高い。しかし、弱点も多い。 asynchronous communication has higher concurrency, but has many drawbacks.

  1. 転送完了までメッセージ・バッファを書き換えられない no modifications to a message buffer.
  2. 送信側は、転送が終了したかわからない A sender cannot know the completion of transmission.
1. は、バッファリング(buffering)で解決可能。しかし、無駄なコピー (copying)、フロー制御(flow control)の問題がある。

2. は、転送完了割り込み(interrupt)で解決可能。プログラミングが難しい。 割り込みよりは、マルチスレッドがよい。

プログラミングスタイルの選択 programming styles

◆バッファリング(buffering)

コピー(copy)するかしないか。

同期、非同期とは直行しているとも言えるが、普通は同期の場合にはバッファ リングを省略してコピーを減らす。

メールボックス(mailbox)には、しばしば有限のバッファ(bounded buffer)がも うけられる。

バッファがフルだった時にどうするか。 フルの時にだけ送信側をブロックさせる、という方法でいいのか。 In asyncronous primitives, can we block when a buffer is full?

◆帯域予約(bandwidth reservation)

帯域保証とは、たとえば、64k bps のように、毎秒、どのくらいデータ を送ることができるかを保証することである。

普通の固定電話(fixed-line phone)の場合、64k bps の帯域予約(bandwidth reservation)、帯域保証(bandwidth guarantee) がなされている。電話を掛け る人が増えてきたとしても、それが 32k bps に減らされるようなことはない。 その代りに、電話を掛けようとすると「通話中(busy)」というエラーが返り、 通信そのものが行えなくなる。

◆遅延保証(delay guarantee)

send してから receive するまで、最長どのくらいの時間がかかるかを保証す ることである。電話の場合、ある程度の遅延保証は行っているとも言える。テ レビの衛星中継(satellite transmission)のように、遅延が大きい媒体では、 普通の会話はできなくなる。

日本の電話の通信遅延

◆単方向/双方向(unidirectional/bidirectional)

単方向の通信しかできない場合でも、単方向の通信を2つ逆方向で組み合わせ ることもできる。

◆メッセージの順序の保証(preserving order of messages)

メッセージをsendした時にその通りの順序で receive できるかどうかという 意味である。結合が作られる場合は、普通は順序も保証される。

◆TCP/IP

TCP/IP は、性質が違う2つのプロトコルから構成されている。 普通にアプリケーションが使うのは、上位の TCP。

◆IP (Internet Protocol)

IPデータグラム転送サービス (datagram transport service)

◆TCP (Transmission Control Protocol)

ストリーム転送サービス (stream transport service) データの区切りが保存されると、sequenced packet と呼ばれる。

◆UDP (User Datagram Protocol)

IPと同じく、データグラム(datagram)転送サービス。 IPと違うのは、アドレス指定のみ。

◆どの通信プリミティブを使うか/choosing communication primitives

分散システムを構築する時に、どの通信プリミティブを使うか 独自の例 examples of specific protocols
順序付きパケット (sequenced packet)。
ストリームと似ているが、メッセージの区切りが保存される(preserving boundaries of messages)。 TCP で、メッセージ本体の前に、バイト数を送ることで実装できる。 sized_io library (initport()).
信頼性のあるデータグラム (reliable datagrams)
通常のデータグラムと似ているが、メッセージが紛失した時に再送する(retransmission)。

◆まとめ(summary)

TCP IP UDP イーサネット 電話 郵便
send   非同期 非同期 非同期 非同期 非同期 非同期
receive  同期 (非同期)* 同期 (非同期)* 同期 非同期
信頼性 あり なし なし なし あり なし
アドレス指定 間接 間接 間接 間接 間接 直接
結合   あり なし なし なし あり なし
方向 双方向 単方向 単方向 単方向 双方向 単方向
マルチキャスト 不可 可能 可能 可能 可能 不可
帯域保証 なし なし なし なし あり なし
* OS 内。

■Socket API

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

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

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

    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.

◆ソケットAPIの主要なシステムコール、または、ライブラリ関数/Important system calls and library functions in Socket API

名前(name) 説明(description)
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 アドレスを扱う関数/functions that manipulate IP addresses

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

◆講義用TCP Library(講義用のTCP接続を作成するためのライブラリ)/TCP library functions for the class System Program of coins

情報科学類の講義、 システムプログラムで 新城が例題を示すために作成した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/Socket API in Java

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

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

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

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

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

■marshaling/unmarshaling

プログラム中のデータ項目とネットワーク上を流れるメッセージに対応づける。 Map data structures in memory to messages over a network.
marshaling (整列化)
メモリ中からデータ項目を集めて、ネットワークでメッセージとして 転送するのに適した形式にまとめる。
unmarshaling (非整列化)
逆。
英語の綴りは、l が1つのものと2つのもの(イギリス綴り)がある。教科書によって違う。

プロセスAがメモリ中のデータを1個にまとめてネットワークに送り出す。プロセスBが受け取り元に戻す

図? marshalingとunmarshaling

4個の要素からなる構造体(a structure with four components)を整列化して送信している。 整列化する基本的な方法 fundamental method unmarshaling ネットワークからデータを受け取ると、先頭から解釈して元のデータを再現す る。

ネットワーク上を流れている時には、整列化されたデータの先頭にはネットワー クのヘッダ(network headers)が付加されている。

◆整数のmarshaling/marshaling an integer

分散プログラムでは、メッセージを送信するプロセスと受信するプロセスが異 なる CPU で実行されることがある。整数をmarshalingする時には、次のような 点を考慮する必要がある。 厳密な規格では、バイト(byte) の代わりに (octet) を使う。

◆バイト・オーダ(byte order)

整数を送るだけでも、バイトオーダに気をつける必要がある。

C言語で扱える整数 Integers in a C languages

(現在のコンピュータのほとんどは、バイト単位でアドレスを付けているので、 1バイトの整数については、バイトオーダの問題はない。)

2バイト以上の整数をメモリに保存する方法: メモリの下位番地 に上位バイト(most significant byte)を置くか下位バイトを置くか

リトルエンディアン (little endian)
下位番地に下位バイトを置く。x86 (Intel Pentium/Core, AMD Athlon/Phenom)。
ビッグエンディアン (big endian)。
下位番地に上位バイトを置く。PowerPC, SPARC, m68k
PowerPC は、両方切り替え可能だが、ビッグエンディアンで使うことが多い。

◆0x2000番地に0x12345678を保存/saving 0x12345678 to the address 0x2000

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

0x12345678を2000番地に保存する。

図? バイト・オーダ(byte order)

◆ビッグエンディアンとリトルエンディアンの比較/comparision of big endian and little endian

◆送り方/transmitting an integer

現在、ネットワーク・バイト・オーダとしては、ビッグエンディアンが広く 使われている。

◆バイトオーダを変換するライブラリ関数/ library functions for converting 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ビット

◆htonl() を使った整数の送信/ sending an integer with htonl()

unsigned long int hostlong, netlong;    
hostlong = 0x12345678 ;    
netlong = htonl( hostlong );    
send(conn, &netlong, sizeof(netlong), 0);

◆snprintf()/strtol()

snprintf() で文字列に直して送り、strtol() や atoi() でもどす方法もある。 文字列文化。インターネットのアプリケーションでよく使われる。

送信側(sender):

    char buf[BUFSIZE];
    hostlong = 0x12345678 ;    
    snprintf(buf,BUFSIZE,"%d\n",hostlong );
    send(conn, buf, strlen(buf), 0);
思ったほど遅くはない。

注意:sscanf() は、整数をデコードするために使う分には問題ないが、文字列 を受け取るために使うとバッファ・オーバーフロー(buffer overflow)が生じる 可能性があるので、使わない方がよい。

◆文字列のmarshaling/marshaling strings

Unicode BOM (byte order mark) 0xffef。

◆XDR

SunRPC (後述) で使われているデータ形式。バイナリ文化。

rpcgen というスタブ・コンパイラがある。 データ構造を与えると、marshaling を行う手続きを自動生成する。

SunRPC を使わなくて、XDR だけを使う方法もある。

xdrmem_create(XDR *, const caddr_t, const uint_t, const enum xdr_op)
メモリの指定されたの番地に保存/回復/メモリの解放。
void xdrstdio_create(XDR *, FILE *, const enum xdr_op)
FILE * を通じて、構造体の読み書き。

◆その他(others)

■クライアント・サーバ・モデル/Client Server Model

通信を構造化(structuring of interprocess communication)。 send()/receive() を直接使うのは、goto (jump) でプログラムを書くようなも の。call/if/while で書きたい。

プロセスを2種類に分類する。通信は、次のパタンを繰り返す。 Classify processes into two types.

クライアント client
先にメッセージ(要求(request message))を send() 1回、後でメッセージ(応答(reply message, response message))を receive() 1回
サーバ server
先にメッセージ(要求(request message))を receive() 1回、後でメッセージ(応答(reply message))を send() 1回
send() の回数と receive() の回数は同じ。相互に繰り返す。

図? send(),receive()と繰り返すクライアントと receive(),send() と繰り返すサーバ

図? 通信のパタンからみたクライアントとサーバの定義

◆クライアントとサーバに分けて考える利点/advantages of classifying processes into clients and servers

混沌としたプロセス間通信(chaotic interprocess communication)を「構造化(structuring)」してわかりやすくする。

図? プロセス5つ、構造化されていない通信パタン

図? 構造化されていないもの(unstructured communication)

図? プロセス5つ、構造化された通信パタン

図? 構造化されたもの(structured communication)

構造化プログラミング(structured programming):制御構造(control flow)で 分かりにくいgoto文をつかわないで、わかりやすいgoto文だけ使う。

◆サービスの授受/providing and receiving services

元々の意味
クライアント(client)
サービスを受ける方、顧客 service recipient
サーバ(server)
サービス(service)を提供する方 service provider

図? サービスの授受によるクライアントとサーバの定義

図? サービスの授受によるクライアントとサーバの定義/definition of client and server by service providing and receiving

◆利用者数/ number of users

サービスを提供する方は、1つのプログラム(コンピュータ)で複数の利用者(multiple users) の面倒をみる。その結果、1台のサーバに複数のクライアント(multiple clients)がつながる。

クライアント client
一人で使うもの used by a single user
サーバ server
複数人で共有するもの used by multiple users

図? 複数のクライアントによるサーバの共有

図? 複数のクライアントによるサーバの共有/sharing of a server by multiple clients

◆接続方法/connection establishment

TCP/IP の通信では、通信を始める前に、まず、 結合(connection)を作る作る必要がある。 これは、電話で話をする前に、まず、電話をかける操作(making a telephone call)を行うことと似ている。
クライアント client
電話を掛ける方に相当する caller of a telephone call
サーバ server
電話を待っている方 callee of a telephone call

以上のように、クライアントとサーバは、いろいろな意味で使われる。これら の意味は、多くの場合、一致しているが、一致していないこともある。

◆能動的・受動的/passive and active objects

通信を開始するパタンで、コンピュータ、プログラム、人間は、次の2つに分 類される。

能動的(active)
ほっといても自分でメッセージを発信し始める
受動的(passive)、受け身
何か言われると答えるが、自分ではメッセージを発信し始めることはない
クライアントとサーバから作られたシステムは、クライアントが能動的になり、 サーバは、受動的になることが多い。

図? 能動的なクライアントと受動的なサーバ

図? 能動的なクライアントと受動的なサーバ/active clients and a passive server

例:Webサーバは、WWWクライアントから何か要求が来ない限り、ずっと 黙っている。

コンピュータを使う時には、人間が能動的になり、コンピュータが受動的にな る。

テレビを見ている時には、人間が受動的になり、テレビが能動的になる。

講義形式の授業では、サービスの授受では、教官がサーバで、学生がクライア ントになる。通信の開始の方法では、教官が能動的になり、学生が受動的にな る。

大学以上では、学生は、能動的になることが求められている。

◆Peer to Peer (P2P)

P2P (Peer to Peer) という用語の意味は、怪しい。

混沌とした通信を 構造化(structuring) してわかりやすくしたものが、クライアント・サーバ・モデル(a client server model)である。

サーバあるシステムでは、サーバが落ちるとシステム全体が動作しなくなる。 このように複数の要素から構成されているシステムで、ある要素が故障した時 に、全体が動作しなくなるような場所を、単一障害個所(single point of failure) という。

コンピュータサイエンスでは、古くから、単一障害個所を避けるための研究が 行われてきている。もっとも成功している方法は、サーバを複数(multiple servers)用意する方法である。

サーバがないシステムでは、下手に作るとどの要素が故障してもシステム全体 が止まってしまうことになる。

サーバがないシステムで成功している例はある。

peer は、「対等の仲間」の意味。「通信相手」という意味もある。

サーバがない方法の利点(特徴)

サーバがない方法の問題点

◆RPC

クライアント・サーバ・モデルに基づくプロセス間通信で、 手続き呼出しの形に見えたら RPC (Remote Procedure Call) ( 遠隔手続き呼び出し )

■クライアント・サーバ・モデルに基づく分散システムの例/An example of a distributed system based on a client server model

ゲームのハイスコア(得点の高い人n人の得点と名前)の維持。 Holding the high score of a game.

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

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

次の2つの機能を、クライアント・サーバ・モデルに基づき、TCP/IP を使って 実装する。 これを、Socket API を提供しているネットワーク・オペレーティング・システム で動作させたい。

◆実行例

$ mkdir hiscore-tcp [←]
$ cd hiscore-tcp [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/add-hiscore.c [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/highscore-client.c [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/highscore-server.c [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/highscore-tcp.h [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/marshaling-burffer.c [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/marshaling-burffer.h [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/ex/show-hiscore.c [←]
$ wget http://www.cs.tsukuba.ac.jp/~yas/cs/csys-2017/2017-10-27/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プロトコル/The hiscore protocol

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

hiscoreで用いる要求メッセージの形式 request message format

hiscoreで用いる応答メッセージの形式 reply message format

◆メッセージの例/message example

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

図? 要求メッセージの例

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

図? 応答メッセージの例

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

[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_*/

◆モジュールの構成/hiscore modules

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

図?モジュールの構成

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

この講義用に新城が作成したもの。次の関数で操作する。
int marbuf_init( marbuf_t *mb, size_t len )
マーシャリング/アンマーシャリング用のバッファを確保する。バッファ・サイズは、len バイト。 最大 len バイトのメッセージを送受信できる。
void marbuf_final( marbuf_t *mb )
マーシャリング/アンマーシャリング用のバッファを解放する。
int marbuf_send_message( marbuf_t *mb, int socket )
マーシャリングしたメッセージを socket を使ってネットワークへ送信する。 メッセージの先頭には、メッセージのバイト数を付ける。
int marbuf_receive_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 バイトのバイト列を(そのまま)取出す。 バイト列は固定長で、長さを知っている必要がある。
基本的な使い方(usage): メッセージのマーシャリングと送信 marshaling and sending a message
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 );

◆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クライアント/hiscore client

[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, size_t 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: }

◆TCP Library

 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 ) {
...
  • 講義用TCP Library の一部。

    ◆hiscoreサーバ/hiscore server

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

    [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サーバのその他の関数/other functions in hiscore server

     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 ) {
    ...
    

    ◆マーシャリング・バッファ・ライブラリの実装/implementation of marshaling buffer library

    [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]

    ■課題(assignment)4 プロセス間通信、マーシャリング、クライアントサーバモデル/ Interprocess communication,marshaling,client-server model

    次の内容を含む「テキスト」ファイルを作成し、 レポート提出ページ から提出しなさい。

    回答は、講義内容にそったものにしなさい。信頼できないソースから得た情報 を回答しないこと。

    締切りは、2017年11月1日、 23:59:59 とする。

    ★問題(401) プロセス間通信としてのTCPの性質/The features of TCP as an interprocess communication primitive

    TCP を Socket API で利用した時、どのようなプロセス間通信のプリミティブとしてどのような性質があるか。 各項目から1つ選びなさい。 Consider the features of using TCP through Socket API as an interprocess communication primitive. Choose one of each item.

    ★問題(402) バイト・オーダ/byte order

    htonl() を使った整数の送信では、バイ ト・オーダを考慮しながら、32ビットの整数(4バイトの整数, long型)を送信す るプログラムの一部を示した。これに対して、 次のプログラムは、バイト・オーダを考慮しながら、32ビットの整数(4バ イトの整数, long型)を受け取るプログラムの一部である。空欄を埋めて、プ ログラムを完成させなさい。ただし、conn は、TCP/IPにより実現されたスト リームを指定するファイル記述子、receive() は、データを受信するシステム・ コールである。

    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.
    

    ★問題(403) Marshaling

    highscore-server の中で、 クライアントが put_score_client() で発した要求メッセージをアンマーシャリング している部分だけを抽出しなさい。以下の /*(A)*/ から /*(H)*/ の欄を埋め なさい。以下のプログラムでは、if 文や switch 文を全て省略してある。

    From the program highscore-server, extract the lines that perform unmarshaling a request message from a client with put_score_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)*/ );
    


    Last updated: 2017/10/27 11:25:15
    Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>