TCP/IPアプリケーションが起動されているかどうかを
検査するために接続の試行を行う場合がある。
例えば IBM iから別のサーバーへFtp接続したい場合は
そのサーバーが PORT 21番で接続可能であるか
テストすればよい。
TCP/IPアプリケーションの種類によってPORT番号が
決まっているので PORT番号を指定して接続試行を
すれば、そのアプリが使用可能かどうかを
判断することができる。
さて接続試行は具体的な関数としてC言語では
connect関数を使って接続を試みることになる。
しかし首尾よく接続できればよいのだが
接続できない場合でも connect関数は数分のあいだ
サーバーからの応答を待機する。
一般的には 3~5分待機した後で失敗を告げるのだが
これは実用的にはあまりにも長い待ち時間である。
人間が待つ限界としてはせいぜい 5~10秒くらいであろう。
待機時間はTCP/IPサーバーの設定値であるが
IBM i から接続するときはクライアントとなるIBM i側で
setsockopt関数などで待機時間を設定することは
できない。
UNIXでは可能であるがTCP/IPの待ち時間を設定できる
唯一の関数は select関数であるとIBMの英文マニュアルには
記述されている。
そこでここでは connect と selectを使った connect関数の
タイムアウト機能を紹介する。
IBM iで待ち時間のある接続試行はソフトウェア製品には
なくてはならないものである。
[ TESTSEL3: TCP/IPでのタイムアウト ]
ソースはこちらで
0001.00 #include0002.00 #include 0003.00 #include 0004.00 #include 0005.00 #include 0006.00 #include 0007.00 #include 0008.00 #include 0009.00 #include 0010.00 #include 0011.00 #include 0012.00 0013.00 #define TRUE 0 0014.00 #define FALSE -1 0015.00 #define TCP_LEN 1492 /* TCP/IP 送受信長 */ 0016.00 int SMB_PORT = 445; /* WINDOWS SMB サーバー */ 0017.00 int m_timeout = 10; /* IE 切断後の再 READ のタイムアウト秒 */ 0018.00 char IPADDR[16]; 0019.00 typedef struct { 0020.00 int BYTESPRO; 0021.00 int BYTESAVL; 0022.00 char MSGID[7]; 0023.00 char RESRVD; 0024.00 char EXCPDATA[100]; 0025.00 } ERRSTRUCTURE; /* Define the error return structure */ 0026.00 ERRSTRUCTURE errcode;/* Error Code Structure for RCVMSG */ 0027.00 0028.00 void main(void){ 0029.00 int sockfd, on = 1, rc, port = 3010, n, usec = 10, flags, len; 0030.00 struct sockaddr_in iaddr; 0031.00 int iaddrlen = sizeof(iaddr); 0032.00 struct timeval timeout; 0033.00 fd_set read_fd, write_fd; 0034.00 char buff[TCP_LEN+1]; 0035.00 fd_set rset, wset; 0036.00 struct timeval tval; 0037.00 0038.00 printf("** TESTSEL3 : select のテスト・サンプル 3 **n"); 0039.00 getchar(); 0040.00 strcpy(IPADDR, "192.176.233.93"); 0041.00 /*strcpy(IPADDR, "192.168.1.93");*/ 0042.00 m_timeout = 10; /* 10 秒のタイムアウト */ 0043.00 0044.00 sockfd = socket(AF_INET, SOCK_STREAM, 0); 0045.00 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); 0046.00 memset(&iaddr, 0, sizeof(struct sockaddr_in)); 0047.00 iaddr.sin_port = htons(SMB_PORT); /* SMB ポートで接続を試みる */ 0048.00 iaddr.sin_family = AF_INET; 0049.00 iaddr.sin_addr.s_addr = inet_addr(IPADDR); 0050.00 0051.00 /* nonblock に設定 */ 0052.00 flags = fcntl(sockfd, F_GETFL, 0); 0053.00 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 0054.00 0055.00 printf("[%d] %s に接続します。 n", __LINE__, IPADDR); 0056.00 getchar(); 0057.00 rc = connect(sockfd, (struct sockaddr *)&iaddr, sizeof(iaddr)); 0058.00 printf("[%d] connect rc = %dn", __LINE__, rc); 0059.00 if(rc == 0) goto DONE; 0060.00 FD_ZERO(&read_fd); 0061.00 FD_SET(sockfd, &read_fd); 0062.00 write_fd = read_fd; 0063.00 timeout.tv_sec = m_timeout; 0064.00 timeout.tv_usec = 0; 0065.00 printf("** (%s) select 待機中 **n", IPADDR); 0066.00 rc = select(sockfd+1, &read_fd, &write_fd, NULL, &timeout); 0067.00 if(rc < 0){/* select 失敗 */ 0068.00 fprintf(stderr, "%d:SELECT 失敗 :%s", __LINE__, strerror(errno)); 0069.00 close(sockfd); 0070.00 getchar(); 0071.00 exit(-1); 0072.00 }/* select 失敗 */ 0073.00 if(FD_ISSET(sockfd, &read_fd) || FD_ISSET(sockfd, &write_fd)){ 0074.00 } 0075.00 else{/* NODATA-TIMEOUT */ 0076.00 printf("%d 秒の受信待機はタイムアウトで終了しました。 n", m_timeout); 0077.00 close(sockfd); 0078.00 getchar(); 0079.00 exit(-1); 0080.00 }/* NODATA-TIMEOUT */ 0081.00 DONE: 0082.00 fcntl(sockfd, F_SETFL, flags); 0083.00 close(sockfd); 0084.00 printf("[%d] (%s) 接続に成功しました。 n", __LINE__, IPADDR); 0085.00 getchar(); 0086.00 exit(0); 0087.00 0088.00 }
[解説]
タイムアウトは
0042.00 m_timeout = 10; /* 10 秒のタイムアウト */
として設定されている。
0051.00 /* nonblock に設定 */ 0052.00 flags = fcntl(sockfd, F_GETFL, 0); 0053.00 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
よって socket識別子をノンプロックに設定しておいてから
0047.00 iaddr.sin_port = htons(SMB_PORT); /* SMB ポートで接続を試みる */ 0048.00 iaddr.sin_family = AF_INET; 0049.00 iaddr.sin_addr.s_addr = inet_addr(IPADDR);
によって設定された ipaddr に対して
0057.00 rc = connect(sockfd, (struct sockaddr *)&iaddr, sizeof(iaddr));
で connectすると正常に接続された場合は rc = 0 が戻る。
0060.00 FD_ZERO(&read_fd); 0061.00 FD_SET(sockfd, &read_fd); 0062.00 write_fd = read_fd; 0063.00 timeout.tv_sec = m_timeout; 0064.00 timeout.tv_usec = 0;
によって selectに必要な socket識別子に対してタイムアウトも設定してから
0066.00 rc = select(sockfd+1, &read_fd, &write_fd, NULL, &timeout);
で、タイムアウトをともなう受信を行う。
この例では
0016.00 int SMB_PORT = 445; /* WINDOWS SMB サーバー */
によって PORT番号 445番の SMBサーバーが待機しているかどうかを
調べている。
SMBサーバーとは SMB:Server Message Block というプリンタ共有サーバーの
ことである。
よく「プリンタを共有にする」とかいう言葉を耳にすることがあるが
正しくは「プリンタを共有にするためのSMBサーバーを起動する」ということである。
あるプリンタ・ドライバが共有であるかどうかを検査するために
PORT 445番でそのTCP/IPサーバーにアクセスすればよいことになる。
この技術は the WINDOWSライターに使われていて共有サーバーに接続可能か
どうかを検査するために使用されている。
またSpoolライターVer5.0でのFTP送信にもこの技術が生かされている。