第11章  RPGを使ったサーバープログラム
RPGプログラマーの為のC言語講座

ソケット通信を行うサーバープログラムは通常デーモンとして動作します。

サーバーデーモンは通常、接続をまつ親プロセスがいて、クライアントとからの接続要求を処理し、 クライアントと接続したあとの実際のデータ処理部分は、子プロセスを起動してそちらにまかせ、 自分は、次のクライアント接続要求に備えるという動きをします。

この機能のサポートの為、よく使用されるのは、プロセスまたはスレッドです。

しかし、残念ながらRPGでは、上記のいずれの機構もサポートしていません。

そのため、前項のサンプルプログラム(SOKRPGSVR)では、1つのサーバーPGM当たり1つのクライアント しかサポート出来ませんでした。(1クライアントが接続すると、LISTENポートをクローズして、クライアント との通信を処理するようになっています。このようなプログラムのロジックでは、次のクライアントも接続 出来るようにするためには、別途サーバーPGMを実行する必要があります。)

RPGでは、RPG内でCALLで呼びだしを行うと、そのPGMがリターンするまで、呼び出し元のRPG の実行は止まってしまいます。これでは、次のクライアントの接続要求を処理することはできません。

SBMJOBでバッチJOBとして投入する方法も考えられますが、この方法だと、たしかに、実行は並列が 可能ですが、バッチJOBは、独自のJOBとして起動されるため、ソケットの接続をそのバッチJOBに 渡す方法が難しくなってしまいます。

RPGでの解決策は?

  1. RPGからプロセスサポートのプログラムを呼び出す
    子プロセス起動するためのCのプログラムを作成し、RPGからコールする。
    起動する子プロセスでは、まずCのプログラムが動き、そのプログラムより、指定された RPGを起動する。プロセス間のコントロールなどは、中間に位置するCのプログラムで 行い、RPGでは考慮しなくてよいようにする。

  2. RPGからスレッドサポートのプログラムを呼び出す
    スレッド起動するためのCのプログラムを作成し、RPGからコールする。
    起動するスレッドではでは、まずCのプログラムが動き、そのプログラムより、指定された RPGを起動する。だだし、この方法を取る場合、RPG自体もスレッドセーフな処理を 考慮する必要があると考えられる。

  3. 異なる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プログラムにより、ソケットのクローズを行います。

上記の3.5.6および8.9はLOOPするものとします。

複数のクライアント(SOKRPGCA)の実行が可能です。

複数実行時にWRKACTJOBコマンドで状況をみると以下のようになります。 sokrpgud.gifイメージ

子プロセスのJOBタイプは、BCIとなるため、子プロセスのRPGでは、 対話型の機能は使用できません。つまり、画面を出したりといった対話型の機能はサポートしません。