先の RPG Socketサーバーに続いて、RPG による Socket通信クライアントの例を紹介する。
RPG による Socketクライアントも非常に簡単なものとなる。
SOCKETを作成して、CONNECT するだけで、直ちに通信を開始することができる。
先の C/400で記述されていた Socketクライアントを RPG に書き直しただけである。
読者は RPG での Socketクライアントの例を他で見たことのある人がいればこの記述がいかに
シンプルなものに仕上がっているか、おわかりのことと思う。
H DFTNAME(SOCKCLT) DATEDIT(*YMD/) BNDDIR('QC2LE')
F*****************************************************************
F* SOCKCLT : TEST SOCKET CLIENT
F*****************************************************************
D TRUE S 4B 0 INZ(0)
D FALSE S 4B 0 INZ(-1)
D SOCKFD S 10I 0 INZ(0)
D SOCK_STREAM S 4B 0 INZ(1)
D RC S 10I 0 INZ(0)
D AF_INET S 4B 0 INZ(2)
D INADDR_ANY S 4B 0 INZ(0)
D SO_REUSEADDR S 4B 0 INZ(55)
D TCP_LEN S 4B 0 INZ(1492)
D BUFF S 48A
D PORT S 4B 0 INZ(400)
D IADDR DS QUALIFIED
D SIN_FAMILY 5I 0
D SIN_PORT 5U 0
D S_ADDR 10U 0
D ZERO 8A
D SOCKET_ER S 12A INZ('SOCKET ERR')
D CONNECT_ER S 12A INZ('CONNECT ERR')
D RECV_ER S 12A INZ('RECV ERR')
D SEND_ER S 12A INZ('SEND ERR')
D*( SOCKET のプロトタイプ宣言 )
D SOCKET PR 10I 0 ExtProc('socket')
D ADDR_FAMILY 10I 0 Value
D TYPE 10I 0 Value
D PROTOCOL 10I 0 Value
D*( CONNECT のプロトタイプ宣言 )
D CONNECT PR 10I 0 ExtProc('connect')
D SOCK_FD 10I 0 Value
D SOCK_ADDR * Value
D ADDR_LENGTH 10I 0 Value
D*( SEND のプロトタイプ宣言 )
D SEND PR 10I 0 ExtProc('send')
D SOCK_FD 10I 0 Value
D BUFFER * Value
D BUFF_LENGTH 10I 0 Value
D FLAGS 10I 0 Value
D*( RECV のプロトタイプ宣言 )
D RECV PR 10I 0 ExtProc('recv')
D SOCK_FD 10I 0 Value
D BUFFER * Value
D BUFF_LENGTH 10I 0 Value
D FLAGS 4B 0 Value
D*( CLOSE のプロトタイプ宣言 )
D CLOSE PR 10I 0 ExtProc('close')
D SOCK_FD 10I 0 Value
D*( PERROR のプロトタイプ宣言 )
D PERROR PR ExtProc('perror')
D MSG * Value
C '* SOCKCLT *' DSPLY ANS 1
C*( SOCKET の作成 )
C*--------------------------------------------------------------------+
C EVAL SOCKFD = SOCKET(AF_INET:SOCK_STREAM:0)
C*--------------------------------------------------------------------+
C SOCKFD IFLT *ZEROS
C CALLP PERROR(%ADDR(SOCKET_ER))
C GOTO END
C END
C 'SOCKET OK' DSPLY
C*( CONNECT : サーバーに接続する )
C EVAL IADDR.SIN_FAMILY = AF_INET
C EVAL IADDR.SIN_PORT = PORT
C EVAL IADDR.S_ADDR = INADDR_ANY
C MOVE *ALLX'00' IADDR.ZERO
C*--------------------------------------------------------------------+
C EVAL RC = CONNECT(SOCKFD: %ADDR(IADDR):
C %SIZE(IADDR))
C*--------------------------------------------------------------------+
C RC IFLT *ZEROS
C CALLP PERROR(%ADDR(CONNECT_ER))
C GOTO END
C END
C 'CONNECT OK' DSPLY
C*( SEND サーバーへデータを送信する )
C '*SOCK CLIENT'CAT(P) X'00':0 BUFF
C*--------------------------------------------------------------------+
C EVAL RC = SEND(SOCKFD: %ADDR(BUFF):
C %LEN(BUFF): 0)
C*--------------------------------------------------------------------+
C RC IFLT *ZEROS
C CALLP PERROR(%ADDR(SEND_ER))
C GOTO END
C END
C 'SEND OK' DSPLY
C*( RECV サーバーからのデータを読む )
C*--------------------------------------------------------------------+
C EVAL RC = RECV(SOCKFD: %ADDR(BUFF):
C TCP_LEN: 0)
C*--------------------------------------------------------------------+
C RC IFLT *ZEROS
C CALLP PERROR(%ADDR(RECV_ER))
C GOTO END
C END
C 'RECV OK' DSPLY
C 'BUFF=' CAT(P) BUFF:0 DSP40 40
C DSP40 DSPLY
C '************'DSPLY
C '* SOCKCLT *'DSPLY
C '* E.O.J *'DSPLY
C '************'DSPLY
C*( CLOSE ソケットを閉じて終了する )
C CALLP CLOSE(SOCKFD)
C END TAG
C ' ' DSPLY ANS 1
C SETON LR
C RETURN
コンパイルは CRTBNDRPG ではなく、
CRTRPGMOD MODULE(QTEMP/SOCKCLT) SRCFILE(MYSRCLIB/QRPGLESRC) AUT(*ALL)
CRTPGM PGM(MYLIB/SOCKCLT) MODULE(QTEMP/SOCKCLT) ACTGRP(*NEW) AUT(*ALL)
によって行う。
H 仕様書にバインド・ディレクリー QC2LE が指定されているのでコンパイラーには何も指示する必要はない。
以上によって RPG によって、簡単に TCP/IP通信を行うことは、ご理解して頂けたと思うが、
それではこれですべてであろうか ?
クライアント側は良いとしても実用的なクライアント/サーバー・モデルをRPG によって開発
しようとすれば、やはりサーバー側はマルチスレッドとして複数クライアントからの応答に
耐えるようにしなければならない。
ご存知の弊社製品( Chicago/Spoolライター/EnterpriseServer ... ) などはサーバー側は
すべてマルチスレッドによるC/Sモデルとなっている。
言語はすべて C/400 であるが RPG しか知らない開発者であってもマルチスレッドを開発することは
可能なはずである。
機会を改めて RPG によるマルチスレッド・モデルを紹介することにする。