メインコンテンツへスキップ
検索
ネットワークアプリケーションを書くときには、ノンブロッキングモードとブロッキングモードを使うことができます。ノンブロッキングモードは、アプリケーションが複数のソケットにサービスを提供するなど、複数のことをしなければならない場合に、より柔軟性があり、必要とされます。しかし、アプリケーションがソケットからの読み込みとファイルやキューへの書き込みなど、1つのことしかしない場合は、ブロッキングモードを使用することで、アプリケーションの複雑さを大幅に減らすことができます。ブロッキングモードには小さな問題がありますが、接続に何か問題が発生した場合、アプリケーションはそのことを知ることができません。

 

これはどのようにして起こるのでしょうか?あなたのアプリケーションが recv を呼び出し、クライアントからのデータを待っていると想像してください。クライアントのネットワーク接続は信頼性が低く、クライアントアプリケーションは複数の再送を経験しています。ある時点で、クライアントの TCP スタックは接続を終了する必要があると判断し、クライアントアプリケーションに通知してソケットをクリーンアップします。クライアントアプリケーションは、クライアントアプリケーションに通知してソケットをクリーンアップします。この問題を解決するには、アプリケーションを手動で終了させて再起動するしかありません。アプリケーションを終了すると、接続されたソケットが"TIME_WAIT"状態になり、REUSEADDR ソケットオプションを設定していない限り、アプリケーションをすぐに再起動してリスニングソケットにバインドすることができません。

 

しかし、OpenVOS に recv 呼び出し中にデータを待つ時間の制限を設定するように指示することもできます。その結果、制限時間が過ぎると呼び出しは -1 を返し、 errno は e$timeout (1081) に設定されます。アプリケーションはその後、どのように処理を進めるかを判断することができます。接続を終了させたり、クライアントにもう一度チャンスを与えたり、接続をテストするためにクライアントに何かを送ったり、他のアプリケーションが適切な応答をしたりすることができます。ポイントは、状況の診断と応答があなたのコントロール下にあるということです。
図2のプログラムtimeout_recv.cはその例です。これは2つの引数をとり、リッスンするポート番号とタイムアウトを1/1024秒単位で指定します。図 1 は実行例を示していますが、コマンドライン (太字下線) はエコーされています。私は 10 秒 (10240 / 1024) のタイムアウトを設定していることに注意してください。プログラムは accept を呼び出したときに報告し、もし accept がタイムアウトを返した場合にはタイムアウトを返します。前の段落では触れませんでしたが、タイムアウトは accept 呼び出し時にも機能し、プログラムはそれを実証しています。接続が行われた後、プログラムは recv を呼んだときにタイムアウトか文字で recv が戻ってきた時間を報告します。すべてのタイムアウトメッセージをハイライトしました。

 

時間制限を設定するための呼び出しは OpenVOS (および VOS) に固有のものであることに注意してください。

 

timeout_recv 5647 10240
command line was: timeout_recv 5647 10240
timeout_recv: 12:14:27 calling accept
timeout_recv: 12:14:37 accept returned a timeout
timeout_recv: 12:14:37 calling accept
timeout_recv: 12:14:47 accept returned a timeout
timeout_recv: 12:14:47 calling accept
timeout_recv: 12:14:48 calling recv
timeout_recv: 12:14:58 recv returned a timeout
timeout_recv: 12:14:58 calling recv
timeout_recv: 12:15:00 recv return 1 characters
timeout_recv: 12:15:00 calling recv
timeout_recv: 12:15:10 recv returned a timeout
timeout_recv: 12:15:10 calling recv
timeout_recv: 12:15:16 recv return 1 characters
timeout_recv: 12:15:16 calling recv
timeout_recv: 12:15:23 recv return 1 characters
timeout_recv: 12:15:23 calling recv
timeout_recv: 12:15:30 recv return 1 characters
timeout_recv: 12:15:30 calling recv
timeout_recv: 12:15:38 recv return 1 characters
timeout_recv: 12:15:38 calling recv
timeout_recv: 12:15:46 recv return 1 characters
timeout_recv: 12:15:46 calling recv
timeout_recv: 12:15:56 recv returned a timeout
timeout_recv: 12:15:56 calling recv
timeout_recv: client terminated connection at 12:16:00
ready 12:16:00

 

図 1 - timeout_recv の実行例
/* *****************************************************************
timeout_recv written by NSDavids Stratus CAC
10-04-23 version 1.0 initial release
This is a demonstration of timing out blocking accept and recv calls
and returning control to the program.
***************************************************************** */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define bzero(s, len)             memset((char *)(s), 0, len)
int errno;
void s$set_io_time_limit(short int *, long int *, short int *);
int s$c_get_portid_from_fildes(int file_des);
/* this routine returns the current time as a string */
char * GetCurrentTime (char * szT)
{
time_t tTime;
struct tm *tmLT;
tTime = time ((time_t *) 0);
tmLT = localtime (&tTime);
sprintf (szT, "%02ld:%02ld:%02ld",
tmLT -> tm_hour,
tmLT -> tm_min,
tmLT -> tm_sec);
return (szT);
}
/* this routine sets the time out value on the port associated
with the socket. These calls are VOS specific, this program
is not portable to other environments. i.e. Windows or Linux */
int SetTimeOut (int iSocket, int iTimeLimit, char * szType)
{
long lPortID;
short sPortID;
long lTimeLimit;
short sError;
if ((lPortID = s$c_get_portid_from_fildes(iSocket)) == -1)
{
printf ("timeout_recv: Error getting port ID of %s socketn", szType);
exit (-1);
}
sPortID = (short)lPortID;
if (iTimeLimit > 0)
{
lTimeLimit = iTimeLimit;
s$set_io_time_limit (&sPortID, &lTimeLimit, &sError);
if (sError != 0)
{
printf ("timeout_recv: Error %d setting time out of %s socketn",
szType, sError);
exit (sError);
}
}
return (0);
}
main (argc, argv)
int    argc;
char   *argv [];
{
int    iCliLen;
struct sockaddr_in sockCliAddr, sockServAddr;
short sPortNo;
int    iBytes;
int    iTimeLimit;
char   szT [32];
int    iListenSock, iAcceptedSock;
#define BUFFERLEN 100
char   szBuffer [BUFFERLEN];
/* process the arguments */
if (argc == 3)
{
sPortNo = atoi (argv [1]);
iTimeLimit = atoi (argv [2]);
printf ("command line was: timeout_recv %d %dnn", sPortNo, iTimeLimit);
}
else
{
printf
("nUsage: timeout_recv <port_number> <time_limit in 1/1024s of a sec)n");
exit (-1);
}
/* set up a listening socket */
if ((iListenSock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
printf ("timeout_recv: Error %d can't open stream socket", errno);
bzero ( (char *) &sockServAddr, sizeof (sockServAddr));
sockServAddr.sin_family        = AF_INET;
sockServAddr.sin_addr.s_addr   = htonl (INADDR_ANY);
sockServAddr.sin_port          = htons (sPortNo);
if (bind (iListenSock,
(struct sockaddr *) &sockServAddr, sizeof (sockServAddr)) < 0)
{
printf ("timeout_recv: Error %d can't bind local addressn", errno);
exit (errno);
}
listen (iListenSock, 5);
SetTimeOut (iListenSock, iTimeLimit, "Listening");
/* In most cases I expect that an application will just block waiting for
a connection. However, I wanted to show that you can also timeout the
call to accept */
iAcceptedSock = 0;
while (iAcceptedSock < 1)
{
printf ("timeout_recv: %s calling acceptn", GetCurrentTime (szT));
iAcceptedSock = accept (iListenSock,
(struct sockaddr *) &sockCliAddr, &iCliLen);
if (iAcceptedSock < 0)
{
if (errno == 1081)
printf ("timeout_recv: %s accept returned a timeoutn",
GetCurrentTime (szT));
else
{
printf ("timeout_recv: %s accept returned the unexpected error %dn",
GetCurrentTime (szT), errno);
exit (errno);
}
iAcceptedSock = 0;
}
}
/* set the timeout on the newly accepted socket */
SetTimeOut (iAcceptedSock, iTimeLimit, "Accepted");
/* main loop, call recv and process the return value. If the return value is
-1 then it is an error. An errno of 1081 (e$timeout) is expected, report
it and continue. Any other error is unexpected and fatal. If the return
value is 0 it means the client terminated the connection, report it
and exit. If the return value is > 0 it indicates the number of
characters received, report it and continue the loop. */
while (1)
{
printf ("timeout_recv: %s calling recvn", GetCurrentTime (szT));
iBytes = recv (iAcceptedSock, szBuffer, BUFFERLEN, 0);
if (iBytes == -1)
{
if (errno == 1081)
printf ("timeout_recv: %s recv returned a timeoutn",
GetCurrentTime (szT));
else
{
printf ("timeout_recv: %s recv returned the unexpected error %dn",
GetCurrentTime (szT), errno);
exit (errno);
}
}
else
if (iBytes == 0)
{
printf ("timeout_recv: client terminated connection at %sn",
GetCurrentTime (szT));
exit (0);
}
else
printf ("timeout_recv: %s recv return %d charactersn",
GetCurrentTime (szT), iBytes);
}
}

 

図 2 - timeout_recv.c
メニューを閉じる

© 2024 Stratus Technologies.