ソフトウェア設計及び演習2017

GLib(GIOChannel)を使った通信プログラム

リポジトリ,コンパイル,実行

まずはサンプルプログラムで動作を確認しましょう.

 svn export https://wiki.cis.iwate-u.ac.jp/svn/csd/kimura/giochannel 
 cd giochannel
 make

班長さんが,自分の端末でサーバを立ち上げます.ポート番号(1000以上の大きい数字)を忘れずに.

 ./server 1234 

班員は,各自,自分の端末からクライアントを立ち上げます.

 ./client 1234 サーバのIP

ここで,ポート番号は班長さんが入力した番号,「サーバのIP」は班長さんの端末のIPアドレスです.(班長さんが自分の端末でifconfig eth0を実行すれば確認できます)

このクライアントは,複数同時に接続できます.

各クライアントは,サーバに対してメッセージ(エントリボックスに入力された文字列)を送り,サーバは,そのメッセージを受け取って('\n'が現れるまで受信して)ウィンドウ上に表示します.クライアントごとに,何かキーボードから打ち込んで試してみてください.

概要説明

通信相手

Cのプログラムからは,通信相手(のプロセス)がどう見えているか?

  • オープンされたファイル,として見えます.具体的にはファイル識別子(ただの整数)です.

相手へメッセージを送るには?

  • ファイル識別子に対して書き込みを行う(writeシステムコールする)と,相手にバイト列が送られます.

相手からのメッセージを読むには?

  • ファイル識別子から読む(この例ではg_io_channel_read_line)と,相手から送られたバイト列が届きます.

メッセージが届いているか確かめる

  • g_io_add_watchを使うと,「ファイル識別子にデータが届いているかどうか」を調べることができます.
    • 同時に複数の通信相手から送られてくるデータの到着も調べることができます.
    • これにより,複数の相手に対する非同期的な通信が実現できます.

サーバとクライアント

socketには,サーバソケットとクライアントソケットが存在します.

  • 接続される方がサーバで,
  • 接続する方がクライアントです,

ソケットの作成

サーバ側

server.cのServerSetup()関数

socketシステムコール

  • ソケットを作成します.
  • エラーがなければ,ファイル識別子が返ってきます.

bindシステムコール

  • ソケットには,sockaddr_in構造体でサーバ側のIPアドレスとポート番号を与えます.
  • ソケットを持ったプロセスと,IPアドレス/ポート番号をもったパケットが結び付きます.
クライアント側

client.cのClientSetup()関数

socketシステムコール

  • サーバのIPアドレスとポート番号を指定して,クライアントソケットを作ります.
  • エラーがなければファイル識別子が返ってきます.
  • 実際に接続されるのはconnectを呼んでから,です.(socketだけでは接続されません)

connectシステムコール

  • サーバに対して接続にいきます.

接続が完了すれば,socketシステムコールで得たファイル識別子に対してread/writeすることでサーバと通信できます.

GIOChannel

例えばGTK+のようなイベント駆動型のプログラムでは,以下のようにプログラミングしなければなりません.

  • メイン・イベント・ループ中で,通信を行うためのファイル識別子を監視しつつ,
  • データが届いたら,対応するコールバックを呼ぶ

これらは,GIOChannel を利用することで比較的簡単に実現できます.

  • GIOChannelは,通信専用というわけではなく,ファイルやパイプ,ソケット等を汎用的に利用するための仕組みです.
サーバ側

ServerSetup()で作ったファイル識別子 s_fd から,IOチャネルを作成

GIOChannel *channel = g_io_channel_unix_new( s_fd );

IOチャネルchannelに対し, データの入力があった場合(G_IO_IN)に関数read_callbackを(引数windowとして)呼び出すようにgtkのメインループに登録

g_io_add_watch(channel, G_IO_IN, read_callback, window);

コールバック関数read_callback中では,IOチャネルchannelからデータを読み込んでtextに格納するように

gchar  *text = NULL;
GError *error = NULL;
g_io_channel_read_line( channel, &text, NULL, NULL, &error);
クライアント側

ClientSetup()で作ったファイル識別子 c_fd から,IOチャネルを作成

GIOChannel *channel = g_io_channel_unix_new( c_fd );

IOチャネルchannelから,それに結びついているファイル識別子fdを(再)抽出

int fd = g_io_channel_unix_get_fd( channel );

ファイル識別子fdに対して,データを書き込み(以下の例では,msgが指すアドレスからlenバイト分を送る)

write( fd, msg, len );

なお,このサンプルでは,一旦クライアントソケットからGIOChannelを作り,そのIOチャネルからまたソケットID(ファイル識別子)を取り出す,という回りくどい方法になっていますが,単なる説明のためですので,実際にプログラミングする場合にはファイル識別子だけを扱うように書き換えても問題ありません.


最終更新日:2017/04/03 09:14:23