第12回(12/18) チャットシステムの作成 ■ チャットシステムの作り方 チャットシステムは、ユーザ同士が計算機端末を用いてキーボード対話をするためのシステムである。1つのチャットサーバと複数のチャットクライアントで構成される。まず、チャットサーバは、クライアントに接続してもらう。次に、クライアントからのメッセージを全てのクライアントに転送する、という処理をする。クライアントは、メッセージをサーバに送ることと、サーバから送られてくるメッセージを画面に表示することを行う。 ■ サーバについて サーバのプログラムは、第8回の宿題1(work0801.rb)の一部改造で実現できる。 改造点は、受信したメッセージをすべてのクライアントに送る、という点である。 # server.rb require 'socket' port = 3456 # ポート番号 clients = 2 # 必要クライアント数 ss = TCPServer.open(port) print "waiting for ",clients," clients\n" csocks = [] clients.times{ csocks << ss.accept } puts "all accepted" while csocks != [] do inputs = IO.select(csocks) if inputs != nil cs = inputs[0][0] if cs.eof? csocks -= [cs] cs.close else msg = cs.gets.chomp csocks.each{|csi| csi.puts msg # すべてに送ってよい } end end end ss.close ■ クライアントについて 接続部、受信部、送信部、終了部、および、ウィジェット群で構成される。 まず、クライアントプログラムを起動すると、接続部のルーチンにより、サーバと接続する。 次に、Tkライブラリを用いて、textウィジェット、送信ボタンウィジェット、終了ボタンウィジェット、および、Entryウィジェットを作る。 具体的に、送信部は送信ボタンウィジェットの 'command' に設けられ、終了部は終了ボタンウィジェットの 'command' に設けられる。両者とも、ソケット通信用のハンドラを接続部よりもらっている。Entryウィジェットは、メッセージを入力するところで、それは、Tk変数を介して送信ボタンから参照される。textウィジェットは、受信部からのメッセージを表示するところである。 ウィジェットを画面に配置し、Tk.mainloop で実行する前に、受信部を子スレッドで実行させる。具体的に、受信部は接続部よりソケット通信用のハンドラを得ており、そこからの入力を待ち、入力があると、textウィジェットにメッセージを表示する、という処理を繰り返す。 ● 接続部 サーバと接続する。ソケット通信ハンドラは、グローバル変数 $mycs に格納する。 $mycs = host と port を指定して TCPSocket をオープン ● 受信部 子スレッドで動作するため、以下のようになる。 def receiver(cs,txt) # cs は接続部、txt はTkTextウィジェット trcv = Thread.fork{ loop{ inputs = IO.select([cs]) if inputs != nil rcvmsg = cs.gets.chomp + "\n" (この message が出力すべき文) insert を使って、textウィジェットに rcvmsg を出力する。 end } } return trcv end ● 送信部 送信ボタンを押したとき、送信部のルーチンが処理される。以下のルーチンを送信ボタンに組み込む。 'command'=>proc{ $mycs.puts sndmsg.value # Entryウィジェットが、tk変数 sndmsg にメッセージを書き込んでいる。 } ● 終了部 終了ボタンを押したとき、終了部のルーチンが処理されるよう、以下のルーチンを送信ボタンに組み込む。 'command'=>proc{ $mycs.close # ソケットを閉じる exit # 全体の終了 } ●textウィジェット txt = TkText.new により作成する。txt は受信部に伝えておくこと。 ●送信ボタンウィジェット 送信部のルーチンを組み込むこと。 ●Entryウィジェット エントリした内容は、tk変数 sndmsg に格納すること。 ●終了ボタンウィジェット 終了部のルーチンを組み込むこと。 ●メインルーチン 以下の順に処理をすれば良い。 1. host = ??? # サーバのホスト名、ポート番号の確保 2. port = ??? # 3. cs = 接続部ルーチンを呼出す 4. tk変数 sndmsg の作成 5. textウィジェットの作成 6. Entryウィジェットの作成 7. 送信ボタンウィジェットの作成(送信部の組み込み) 8. 終了ボタンウィジェットの作成(終了部の組み込み) 9. 受信部の実行を開始する 10.各ウィジェットのジオメトリ指定 11.Tk.mainloop ■■■■■ 総仕上げ ■■■■■ 上記のチャットシステムは、とても地味です。様々な工夫をこらしてみましょう! ・チャットのログを記録すること。 ・サーバの切替をすること。 ・クライアント数を自由個にすること。 ・ハンドルネームを付けること。 ・背景色、文字の色を変えること。 ・トライ辞書を使って、自動的に顔文字を表示すること。 ・ボタンではなく、リターンキーで送信すること。 ・textウィジェット部が書き換えから保護すること。 ・サーバーがバーナー広告を出すこと。数分ごとに更新すること。 などなど