第8回(12/27) ソケット通信 ・ソケットの開設、データ転送、ポーリング ・サーバ、クライアント ■ Ruby から使うには ソケット通信ライブラリ require 'socket' サーバのためのクラス ・TCPServer クラス TCPServer.open(ポート番号) ..... サーバの受付を開始する。ポート番号とは、受付窓口の番号。返り値はサーバ受付のソケット番号(以後 ss とする) ss.accept....................... クライアントを受け付ける。返り値は受け付けたクライアントを担当するためのソケット番号(以後 cs とする) cs.gets......................... クライアントからの情報を受け取る。返り値は受け取った文字列。 cs.puts ....................... クライアントへ情報を送る。 cs.close ....................... クライアントとの接続を閉じる。 ss.close ....................... サーバの受付を止める。 クライアント、一般のためのクラス ・TCPSocket クラス TCPSocket.open(ホスト名,ポート番号)... 指定ホストの指定ポートのサーバと接続する。返り値は、通信のためのソケット番号(以後 cs とする) cs.gets......................... サーバからの情報を受け取る。返り値は受け取った文字列。 cs.puts......................... サーバへ情報を送る。 cs.close........................ サーバとの接続を閉じる。 その他、便利なメソッド Socket::gethostname............. この計算機のホスト名を取得する。 IO.select([操作子, ...])........ 入力の有無を調べる。操作子には、標準入力 $stdin、ソケット番号、オープンしたファイル File::open(〜)の返り値、などがある。これらから入力があると、その操作子の配列を返す。 操作子.eof? .................... 操作子の入力が終端であるかどうかを返す。 ■ 例1:簡単なサーバ&クライアント 1 対 1 ⇒ list0801.rb (サーバ) ⇒ list0802.rb (クライアント) □□□ 演習1 □□□ list0801.rb(サーバ) と list0802.rb(クライアント) を入力し動作を確認せよ。 サーバを実行する kterm と、クライアントを実行する kterm が必要。 ■ 例2:受付係と担当者型サーバ 受付係は、クライアントの接続を待つ(accept)。接続があると、担当者をたてて(子スレッド)、それにクライアントとの応対をまかせる。複数のクライアント接続が可能。 ⇒ list0803.rb(サーバ) □□□ 演習2 □□□ list0803.rb(サーバ) と list0802.rb(クライアント) の動作を確認せよ。 ■ 例3:サーバからの情報発信 例1、例2では、クライアントからサーバへ情報が流れていた。接続したソケット番号は、情報を入力することも出力することもできる。 次のサーバは「さかさ文字」を返し、クライアントはキー入力とサーバメッセージの表示を交互に行う。 ⇒ list0804.rb(サーバ) ⇒ list0805.rb(クライアント) □□□ 演習3 □□□ list0804.rb(サーバ) と list0805.rb(クライアント) の動作を確認せよ。 list0803.rb のようにスレッドを使い複数クライアントに対処し、list0804.rb のようにさかさ文字を返すサーバを作れ。クライアントは、list0805.rb をそのまま使う。 ⇒ prac0803.rb(スレッド版サーバ) ■ 例4:入力選択つきサーバ スレッドを使うと複数クライアントが簡単に作れるが、クライアント間でデータをやりとりするようなサーバでは同期が必要なため、単一スレッドで処理したほうがわかりやすい。 例題として、必ず2つのクライアントと接続するサーバを作る。このサーバは、片方のクライアントからの情報をもう一方のクライアントに転送する。ここで、クライアントからの情報待ちの方法が問題となる。これには IO.select を使う。 ⇒ list0806.rb □□□ 演習4 □□□ list0806.rb(サーバ) に対応したクライアントを作る。このクライアントは、キーボード入力のあるときは、ソケットを通じて入力文字列をサーバへ送り、サーバからの情報のあるときは、画面に表示する。 これを実現するには、IO.select でキーボード入力($stdin)とソケット番号(例えばcs)の入力待ちをすればよい。この返り値が、$stdin の場合は、キーボード入力であり、それ以外ならばソケット番号とみなして処理をすればよい。 ⇒ prac0804.rb(入出力クライアント) ※ ヒント list0805.rb の while 文を止めて、無限ループを作る。その次の行に select により、$stdin と cs の選択をする。以後は、list0806.rb のとおり各場合について処理をする。 ■■■ 宿題 ■■■ 演習4のサーバとクライアントを強化しよう。 level1 : 5 個のクライアント接続ができるようにサーバを強化する。cs1からcs5まで作ってもよいが、配列変数を使って工夫をしよう。 ⇒ work0801.rb level2 : クライアントと接続中に、新たなクライアントが接続ができるようにサーバを強化する。select のとき受付用のソケット ss も含めれば良い。 ⇒ work0802.rb level3 : (level2ができた人) クラアントで「コントロール D」を入力すると、全体が落ちることを改善する。ヒント:コントロール Dにより、$stdin.eof? は true となる。サーバ側では、クライアントが close されると、対応するソケットcs について cs.eof? が true となる。また、配列変数 x から特定要素 y を取り除くには、x -= [y] とすれば良い。 ⇒ work0803.rb (クライアント), work0804.rb (サーバ) 選択したレベルを明記し、プログラムリスト、手書きコメント、および、動作結果を提出せよ。なお、level1、2 の人はサーバのプログラムのみ、level3の人はサーバ(level2の改良型であること)とクライアントの両プログラムを提出せよ。