ソケット通信を行うサーバープログラムは通常デーモンとして動作します。
サーバーデーモンは通常、接続をまつ親プロセスがいて、クライアントとからの接続要求を処理し、
クライアントと接続したあとの実際のデータ処理部分は、子プロセスを起動してそちらにまかせ、
自分は、次のクライアント接続要求に備えるという動きをします。
この機能のサポートの為、よく使用されるのは、プロセスまたはスレッドです。
しかし、残念ながらRPGでは、上記のいずれの機構もサポートしていません。
そのため、前項のサンプルプログラム(SOKRPGSVR)では、1つのサーバーPGM当たり1つのクライアント
しかサポート出来ませんでした。(1クライアントが接続すると、LISTENポートをクローズして、クライアント
との通信を処理するようになっています。このようなプログラムのロジックでは、次のクライアントも接続
出来るようにするためには、別途サーバーPGMを実行する必要があります。)
RPGでは、RPG内でCALLで呼びだしを行うと、そのPGMがリターンするまで、呼び出し元のRPG
の実行は止まってしまいます。これでは、次のクライアントの接続要求を処理することはできません。
SBMJOBでバッチJOBとして投入する方法も考えられますが、この方法だと、たしかに、実行は並列が
可能ですが、バッチJOBは、独自のJOBとして起動されるため、ソケットの接続をそのバッチJOBに
渡す方法が難しくなってしまいます。
RPGでの解決策は?
- RPGからプロセスサポートのプログラムを呼び出す
子プロセス起動するためのCのプログラムを作成し、RPGからコールする。
起動する子プロセスでは、まずCのプログラムが動き、そのプログラムより、指定された
RPGを起動する。プロセス間のコントロールなどは、中間に位置するCのプログラムで
行い、RPGでは考慮しなくてよいようにする。
- RPGからスレッドサポートのプログラムを呼び出す
スレッド起動するためのCのプログラムを作成し、RPGからコールする。
起動するスレッドではでは、まずCのプログラムが動き、そのプログラムより、指定された
RPGを起動する。だだし、この方法を取る場合、RPG自体もスレッドセーフな処理を
考慮する必要があると考えられる。
- 異なるJOB間で、ソケットディスクリプターを渡せるようにする
Takedescriptor()/Givedescriptor()を使用して、JOB間でソケットディスクリプターを受け渡す
ただし、この関数が、相手のJOBIDを必要とするため、単純なCALLだけでは処理ができない。
が考えられます。
今回は、RPGでの考慮をできるだけしたくないので、
1.RPGからプロセスサポートのプログラムを呼び出す
方法で実装したいと思います。
それでは、まずプロセスを呼び出すCのプログラムを用意しましょう
プロセス関数 | Cのプログラム名 | 説 明 |
spawn() | SKSPWN | 子プロセスの生成 |
.. | SKCHLD | 子プロセス実行 |
上記のPGMはSocket RPGとして、一つのパッケージにまとめました。
それでは実際に、これらのプログラムを利用して、
RPGによるサーバーデーモン型ソケット通信のプログラムを作成してみましょう
作成するサンプルのRPGは、以下の様なプログラムとします。
サーバーデーモンRPG |
クライアントRPG |
1.ソケットのオープン
SKOPENプログラムによりソケットをオープンします。SKOPENでは、setsocketオプションで、
TCP_NODELAY,SO_REUSEADDRをセットしています。
|
1.ソケットのオープン
SKOPENプログラムによりソケットをオープンします。SKOPENでは、setsocketオプションで、
TCP_NODELAY,SO_REUSEADDRをセットしています。
|
2.バインド
SKBINDプログラムによりソケットにIPアドレス/ポート番号を割り当てます。
SKBINDでは、明示的なIP割り当ての代わりにANYとしてどのインターフェースからの
接続要求についても受けとれります。
|
なし |
3.リッスン
SKLISNプログラムにより、クライアントからの接続要求の受け入れ準備をします。
|
なし |
なし |
4.コネクト
SKCONNプログラムにより、サーバーに接続を行います。IPアドレスは、
構内ループバックアドレス(127.0.0.1)を指定しているので、サーバーと同じマシン上で
クライアントも実行すると仮定しています。
|
5.アクセプト
SKACPTプログラムにより、クライアントからの接続要求を受け入れます。
この際に、新しいソケットディスクリプターがシステムにより与えられます。これ以降の通信は
ここで割り当てられた新しいディスクリプターを使用します。
|
なし |
6.子プロセス作成
SKSPWNプログラムにより、パラメーターに子プロセスプログラム(SOKRPGUP)と
5.アクセプトで入手したソケットデイスクリプターを指定し、子プロセスを起動します。
”5.アクセプトへ戻る”
|
なし |
子プロセスRPG |
なし |
7.子プロセス起動
SKSPWNにより呼び出されたSKCHLDプログラムからRPG(SOKRPGUP)が起動されます。
RPGは、SKCHLDプログラムからソケットディスクリプターをパラメーターとして受け取ります。
|
なし |
8.データ受信
SKRECVプログラムを使用し、クライアントから送信された文字列を受信します
|
8.データ送信
SKSENDプログラムにより、画面から入力された文字列を送信します。
|
9.データ送信
SKSENDプログラムにより、8.でクライアントから受信した文字列をそのまま送信します。
(エコー処理)
|
9.データ受信
SKRECVプログラムにより、サーバーから送信された文字列を受信し、画面表示します。
(エコー処理)
|
10.ソケットクローズ
文字列[EXIT]が送られてきた場合は、SKCLOSプログラムにより、ソケットのクローズを行います。
|
10.ソケットクローズ
画面からF3が押されるか、サーバープログラムが終了した場合は、SKCLOSプログラムにより、ソケットのクローズを行います。
|