0
|
1 # Lindaライブラリ入手方法
|
|
2
|
|
3 Linda serverの本体と、通信プログラムの例です。 通信プログラム作成
|
|
4 の参考にして下さい。cvsでは、以下のプロジェクト名でチェックアウトしてください。
|
|
5
|
|
6 Game_project/Linda
|
|
7
|
|
8 # Lindaの解説
|
|
9
|
|
10 ## Lindaとは
|
|
11
|
|
12 Lindaとは、タプルと呼ばれるIDとDataがセットになったものを、各クライア
|
|
13 ントがサーバに対して out,in,rd などのコマンドを用いて読み書きすることによって通信を行うシステムです。 サーバーに蓄えられたデータへのアクセスは、 APIにタプルのIDを指定することによって行います。
|
|
14
|
|
15
|
|
16 ## サーバ (ldserv.c)
|
|
17 Lindaサーバのソースはldserv.cのみです。
|
|
18
|
|
19 サーバは、新規にクライアントが接続すると、タプルスペースのID65535にシーケンスな番号をデータとしてもつタプル
|
|
20 を蓄えます。クライアントがこのID65535からタプルを取得(inを用いる)することで、
|
|
21 クライアント毎にユニークな番号を割り当てることができます。
|
|
22
|
|
23 ## クライアントAPI (lindaapi.c,lindaapi.h)
|
|
24
|
|
25 タプル送受信のAPI(out,in,rdなど)を用いると、タプルはCOMMANDキューに溜められていき、プログラマが指定したタイミングで(psx_sync_n()実行時)、 一気にサーバに送信されます。つまりAPIを実行した段階ですぐに通信が 始まるわけではありません。
|
|
26
|
|
27 また、サーバからのタプルの受信も psx_sync_n() を実行したときに行い
|
|
28 ます。受信したタプルは REPLYキュー に蓄えられ、psx_reply() で取り出します。
|
|
29 また、コールバックを用いて受信したときのアクションを登録することができます。
|
|
30
|
|
31 ## LindaのAPI
|
|
32
|
|
33 - void start_linda(hostname);
|
|
34 - 通信を初期化して、Linda APIが使用可能にする
|
|
35 - void psx_sync_n();
|
|
36 - COMMANDキューに溜められたコマンドとデータをサーバへ送信します。
|
|
37 - また、サーバからデータを取得し、REPLYキューへ溜めます。
|
|
38 - int psx_out(int id, char *data, int size);
|
|
39 - dataが示すアドレスから、指定したIDのタプルへsize byteのデータを書き込むコマンド。\
|
|
40 - 返り値は、このコマンドのsequence番号です。
|
|
41 - int psx_in(int id);
|
|
42 - 指定したID番号のタプルから、データを読み込むコマンド。
|
|
43 - このコマンドを使用した場合、サーバー上に存在するタプルは、 クライアントがデータを読み込んだ後削除されます。
|
|
44 - 返り値として、sequence番号を取得します。
|
|
45 - int psx_rd(int id);
|
|
46 - 指定したID番号のタプルから、データを読み込むコマンド。
|
|
47 - このコマンドを使用した場合、サーバー上に存在するタプルは、 クライアントがデータを読み込んだ後も残ります。
|
|
48 - 返り値として、sequence番号を取得します。
|
|
49 - int psx_wait_rd(int id);
|
|
50 - 基本的に psx_rd() と同じですが、サーバの タプルスペースにデータがあってもすぐには読みこまず、 次にデータが書き込まれるまで待ちます。
|
|
51 - データが書き込まれたときのみそのデータを読みこみ、クライアントへ送信します。
|
|
52 - 返り値として、sequence番号を取得します。
|
|
53 - unsigned char psx_reply(int seq);
|
|
54 - REPLYキューからデータを取り出すときに使用します。
|
|
55 - psx_in/psx_rdで受け取ったsequence番号を引数とし手渡すと、 要求したデータがREPLYキューにある場合、 データが格納されているTUPLEへのポインタを返します。
|
|
56 - psx_replyによって返されたポインタの先には、 先頭にそのTUPLEのヘッダ情報が 12bytes 格納されていて、 その後にデータそのものが格納されています。
|
|
57 - int psx_callback_in(int id,void (*callback)(char *tuple,void *),void *obj);
|
|
58 - サーバからデータを取得した際、REPLYキューに溜めずに任意の関数を 呼び出すよう指定することができます。
|
|
59 - 引き数の callback 関数ポインタは psx_sync_n() の中で(データが到着したら)呼ばれます。
|
|
60 - 上記の psx_in / psx_rd / psx_wait_rd のコマンドの各々に対してpsx_callback_in / psx_callback_rd / psx_callback_wait_rd が用意されています。細かな説明は後の方でします。
|
|
61 - int psx_get_datalength(unsigned char *tuple);
|
|
62 - psx_reply()等で得たTUPLEのヘッダ部分からデータの大きさの情報を得る関数です。
|
|
63 - 他にシーケンス番号、ID、モードの情報を得る関数も用意しています。 (各々 psx_get_seq(), psx_get_id(),psx_get_mode())
|
|
64
|
|
65 ## マクロ
|
|
66
|
|
67 LindaではTUPLEのヘッダ情報やデータ部分へアクセスするためのオフ
|
|
68 セットと して以下のマクロを用意しています。
|
|
69
|
|
70 #define LINDA_MODE_OFFSET 0
|
|
71 #define LINDA_ID_OFFSET 1
|
|
72 #define LINDA_SEQ_OFFSET 3
|
|
73 #define LINDA_DATA_LENGTH_OFFSET 7
|
|
74 #define LINDA_HEADER_SIZE 12
|
|
75
|
|
76 これらはpsx_reply()で得たTUPLE(unsigned char のポインタ)のデータ部分
|
|
77 やヘッダ部分などを取得するときなどに用いられます。データ取得は プログ
|
|
78 ラミングの流れで示します。 以下はpsx_reply()で得たTUPLE (tuple) のヘッ
|
|
79 ダ部分を表示しているところです。
|
|
80
|
|
81 printf("MODE: %c\n",*(char*)(tuple+LINDA_MODE_OFFSET));
|
|
82 printf("ID: %d\n",*(short*)(tuple+LINDA_ID_OFFSET));
|
|
83 printf("SEQ: %d\n",*(int*)(tuple+LINDA_SEQ_OFFSET));
|
|
84 printf("DATA_LENGTH: %d\n",*(int*)(tuple+LINDA_DATA_LENGTH_OFFSET);
|
|
85
|
|
86 ヘッダ情報は異なるエンディアンのCPU上でも対応するように整数の上位 ビッ
|
|
87 トが配列の前の方にくるように揃えているので、予想した結果がでない かも
|
|
88 しれません。ヘッダ部分の情報を得るにはpsx_getから始まる関数群を 使って
|
|
89 下さい。
|
|
90
|
|
91 # プログラミングの流れ
|
|
92
|
|
93 まずは、プログラム初頭でstart_linda(hostname)を 実行して通信を行えるようにしておきます。
|
|
94
|
|
95 ## データを送信する場合
|
|
96
|
|
97 - psx_outでコマンドをキューに追加 (1)
|
|
98
|
|
99 psx_outした時点では、単にキューへためられるだけで、 まだ通信は開始され
|
|
100 ていません。 Lindaでは、char型かunsigned char型でしかデータを送ること
|
|
101 ができません。 ただ、符号の問題を避けるため、char型ではなくunsigned
|
|
102 char型を使いましょう。 少し詳しく言うと、psx_outは、与えられたポインタ
|
|
103 のアドレスから、 1byteずつ順番に指定されたbyte数サーバーへ送信するとい
|
|
104 う事です。 そこで、送りたいデータを格納している変数や構造体等が
|
|
105 unsigned char型以外である場合、unsigned char型に タイプキャストしてや
|
|
106 る必要があります。
|
|
107
|
|
108 - psx_sync_nで実際にコマンドを実行 (2)
|
|
109
|
|
110 この時、今までキューにためこまれたLindaのコマンドが、一気に実行されま
|
|
111 す。 PlayStation 2では、描画に関する演算はEmotion Engineの中で行われ
|
|
112 ますが、実際の描画処理はGraphics Synthesizerで行われます。 つまり、描
|
|
113 画処理が行われている間、Emotion Engineには余裕ができます。 その時間を
|
|
114 使用して、通信を行うようにプログラミングしましょう。 (そのタイミングを
|
|
115 考慮して、psx_sync_nを実行しましょう)
|
|
116
|
|
117
|
|
118 - 受信相手のデータ受け取りの確認 (3)
|
|
119
|
|
120 通信を行う際、待ち合わせを行い、 データを受け取るまでゲームが止まって
|
|
121 しまってはいけないので、 Lindaサーバーでは非同期通信を採用しています。
|
|
122 もし送信するデータを確実に相手に受け取って欲しい場合は、 相手から受け
|
|
123 取りのサイン(Ack)を受け取るようにするとよいでしょう。
|
|
124
|
|
125 例えば、送信側が psx_out() したデータを受信側が psx_in() で受け取 るよ
|
|
126 うな通信を何回か繰りかえす場合を考えます。 このとき、通信相手がデータ
|
|
127 を受け取った事を確認してから、 次のデータを送らなければなりません。 何
|
|
128 も考えずにどんどんpsx_outを行うと、タプルにまだデータが残っているので
|
|
129 新たなデータを書き込めず、キューにためこまれていきます。 そして、処理
|
|
130 がどんどん重くなってしまい、ゲーム自体もスピードが落ち、 キューがいっ
|
|
131 ぱいになると、失敗するようになってしまいます。 そこで、その確認を取る
|
|
132 ために、相手がデータを受け取ったという サイン(Ack)を、psx_inやpsx_rdを
|
|
133 使用して受け取ります。 Ackを受け取ってから、次のデータをpsx_outして下
|
|
134 さい。 ただし、プログラムが通信を行う部分でループして、 Ackを得るまで
|
|
135 プログラムが止まるというわけではありません! メインの処理は、Ackを待つ
|
|
136 間も動き続ける書き方をして下さい!! そのために、psx_replyはif文で括ら
|
|
137 れているのです。
|
|
138
|
|
139 - (1)からを繰り返し行う (4)
|
|
140
|
|
141 ## データを受信する場合(callbackを使わない)
|
|
142
|
|
143 - psx_in/psx_rd/psx_wait_rdでsequence番号を取得 (1)
|
|
144
|
|
145 sequence番号とは、実行したコマンドに与えられる、 整理券の番号みたいな
|
|
146 ものです。
|
|
147
|
|
148 - psx_replyでデータの到着を監視 (2)
|
|
149
|
|
150 データが来ていれば、それに対する処理を実行する。 来ていなければ、その
|
|
151 ままプログラムの処理を進める。
|
|
152
|
|
153 - データを任意の変数・構造体に展開 (3)
|
|
154
|
|
155 データを任意の変数・構造体に展開する際、 データを送信するために 1byte
|
|
156 ずつに分解されたデータ を、元の形に組み立てる必要があります。
|
|
157 psx_reply() で得たポインタ(仮に reply とします)の 指す先にはTUPLEのヘッ
|
|
158 ダが含まれているので、 実データ(RealData型とします)は
|
|
159
|
|
160 (RealData *)(reply + LINDA_HEADER_SIZE)
|
|
161
|
|
162 でアクセスできます。LINDA_HEADER_SIZE は 12 に置き換えられます。
|
|
163 memcpyを使用すると便利です。
|
|
164
|
|
165 - psx_replyで受け取ったポインタをfreeする (4)
|
|
166
|
|
167 通信で受け取ったデータは、psx_sync_n()の実行時にmallocされ、 メモリの
|
|
168 何処かへ蓄えられています。 それを放っておくと、データを受け取る度に、
|
|
169 そのデータ分だけ メモリを消費し続ける事になり、メモリ不足の原因となり
|
|
170 ます。 よってデータを任意の変数・構造体に展開し終えた後に、 psx_reply
|
|
171 で得たポインタをfreeして下さい。 勿論、データを任意の変数・構造体に格
|
|
172 納せずに、 psx_reply で得たポインタをそのまま使用しても構いません。
|
|
173
|
|
174 - 送信相手へAckを送る (5)
|
|
175
|
|
176 必要ならば データを受け取ったというサインを送信者へ送ります。 送信者が
|
|
177 Ack だと分かるものなら、送るデータは何でも構いません。
|
|
178
|
|
179 - (1)からを繰り返し行う (6)
|
|
180
|
|
181
|
|
182 ## データを受信する場合(callbackを使う)
|
|
183
|
|
184 - psx_callback_in / psx_callback_rd / psx_callback_wait_rd を使う (1)
|
|
185
|
|
186 callbackを使う場合は引き数にcallbackする関数と、 その関数の引き数にす
|
|
187 る構造体を指定します。この callbackする関数の型は
|
|
188
|
|
189 void function(char * tuple, void * obj);
|
|
190
|
|
191 となっています。objは任意の構造体のポインタです。 この関数の内部の処理
|
|
192 は自由に作ってもいいのですが、 REPLYキューに蓄えられないので、
|
|
193 psx_reply()を実行しても 答えは返ってきません。 引き数の tuple は
|
|
194 psx_sync_n() 内で malloc されているので、 いらなくなったら free() して
|
|
195 ください。
|
|
196
|
|
197 データを受け取ったという情報をAckとして 送る場合などは、この
|
|
198 function() 内で送信する必要があります。
|
|
199
|
|
200 この callback 関数を上手く書くと、psx_reply() がいらない ようなプログ
|
|
201 ラムも可能です。
|
|
202
|
|
203 # Linda's Tips
|
|
204
|
|
205 Lindaでのプログラミングの際、以下のことに留意しておいて下さい。
|
|
206
|
|
207 - 通信プログラムの例題が、Game_project/Linda/example にあります。
|
|
208 - IDの幅は、0〜65534です。
|
|
209 - 65535のIDでpsx_inすると、Lindaサーバーから個別のIDを得られます。
|
|
210 - IDは、1から順に与えられます。また、IDはASCII文字で送られて来るので、 atoi 関数で数値に変換して下さい。
|
|
211 - 一度に送れるデータは、1つのタプルにつき、2^32-12bytes(intの最大値-ヘッダサイズ)です。
|
|
212 - NULLは送らないで下さい。
|
|
213 - コマンドをためすぎてキューがいっぱいになると失敗します。
|