TCP/IP

31. RPG で作成する TCP/IP Socket 通信クライアント

先の RPG Socketサーバーに続いて、RPG による Socket通信クライアントの例を紹介する。
RPG による Socketクライアントも非常に簡単なものとなる。
SOCKETを作成して、CONNECT するだけで、直ちに通信を開始することができる。
先の C/400で記述されていた Socketクライアントを RPG に書き直しただけである。
読者は RPG での Socketクライアントの例を他で見たことのある人がいればこの記述がいかに
シンプルなものに仕上がっているか、おわかりのことと思う。

【 RPG による TCP/IP 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 によるマルチスレッド・モデルを紹介することにする。