サンプルプログラム(1)を自分の環境で動作するようにし,実行結果を示すとともに,
プログラムの中で使われている関数(Connect,Disconnect,Send Data,Recv Dataなど)
の動作を説明し,サーバ・クライアント動作全体をフローを示して説明せよ.
今回はサンプルプログラムのclient.cのプログラムを少し変更した。
変更後のプログラムは
こちら。
ちなみに変更点はホスト名を引数で取るようにしたこととget関数のwarningを消すために
fgetsを使用した点の2つのみである。server.cのプログラムに変更はない。
以下はこの2つのプログラムを動作させた結果である。
client実行例
[nw0413:~] j04013% ./client nw0413.st.ie.u-ryukyu.ac.jp
Connected.
Input Keyword = 123
Keyword = [123] / Data = [456]
Input Keyword = kazu
Keyword = [kazu] / Data = []
Input Keyword = xxxx
Keyword = [xxxx] / Data = [yyyy]
Input Keyword =
Disocnnected.
このプログラムはinetdを用いることでサーバ側ではソケット生成を行わないで通信を行っている。
client.cのプログラムは以下のような関数の働きによってソケット通信を行っている.
Connect()関数
Connect()関数はサービスの選択,ホスト名の取得,ソケットの作成,サーバーへの接続を担っている.
それらの働きは以下の関数によって実現されている。
- getservbyname()
getservbyname()は/etc/servicesのファイルから引数で取られているサービスの名前,
及びプロトコルの種類に一致する行を探し出し,その行の内容を納めたservent構造体
を返す.
- gethostbyname()
ネットワーク上のホストのエントリを取得する.引数はホスト名,あるいはIPアドレスであり,
そのホストに対応するhostest構造体を返す.
- socket()
ソケットの作成を行う.引数はドメインの種類,ソケットのタイプ,使用するプロトコルとなっている.
今回の引数はドメインはAF_INET.ソケットのタイプはSOCK_STREAMでこれは
順序性と信頼性がある双方向のバイト単位でのストリームを提供している.プロトコルは0となっており
ソケットの種類によって確定されるものである.
- bcopy()
指定されたバイト列コピーする.今回はエントリーされたアドレスをソケットのアドレスにコピーするため
に用いられている.
- connect()
ソケットの接続を行う.
Disconnect()関数
Disconnect()関数は通信されているソケットをclose()を用いて閉じることで通信を終了する関数である.
SendData()関数
write()を用いてソケットにデータを書き込む(=データを送信する).
write()は指定されたファイルに指定されたバッファを,指定されたバイトだけ送信する.
今回は指定するバイトは文字列の長さとなっている.write()は返り値として書き込んだバイト数
を返し,その値が文字列の長さと等しいかどうかで送信の成功を判断している.
RevData()関数
RevData()関数ではread()を用いることでデータの受信を行っている.
今回のプログラムでは1バイトずつline+iのアドレスの指す場所に読み込む。
iをインクリメントすることで次の文字の格納場所に移動している.
つまりは一文字ずつ受信し,char型に格納している,この作業は改行コードがでた時点で終了する.
read()の返り値もwrite()と同様にバイト数である.
対するserverプログラムは送信されてきたデータの受信,dataファイルを呼び出して文字列の分割.
それと送られてきたデータと一致するかを判定し,対応した文字列をwrite()を用いてクライアント
側に返す働きをしている.サーバー側はinetdを起動することにより通信を確立しているため,server
プログラムにはソケットの生成を行わなくても良い.
sever.cで関数は以下のような働きをしている.
GetLineFromPeer()
クライアント側からのデータを受信する.データの受信方法はclient.cでの受信方法と同じようにread()
を用いて行っている.
GetKeywordData()
fopenでdataファイルを読み込むために開く.一行ずつ読み込んでエラーがないかチェックし,
エラーがなかった場合にはDELIMITSを用いて文字列を二つに分けてpとppに保存する.その後,クライアント
から送られてきたデータとpとが同じ文字列かを判断し,同じであった場合にはppの値を配列にコピーして
その値を返す.
これらの全体的な流れを以下で説明するとともにそのフロー図を図1に示す.
- クライアントはinetdに接続のためのリクエストを送信する。
- inetdはリクエストがあったらserverを起動し,それとともにレスポンスをconnect()に返す
- データを入力されたクライアントはwrite()をサーバ側はread()を用いて通信する
- サーバ側はdataを呼び出し,通信されたデータを照合し,対応した値を見つける
- write()を用いて通信,クライアント側はRevData()のread()でデータを受けとる
- データを出力し、次の入力を待つ
- 何も入力されなかったときにはサーバでそれを処理した後,クライアントでループをぬけDisconnect
()のclose()によってソケット通信を終了する
図1.サーバ・クライアントの流れ