Linux:タイムアウトのあるソケットからの読み取りまたは受信がありますか?


105

タイムアウト付きのソケットからデータを読み取るにはどうすればよいですか?私は知っています、select、pselect、poll、timeoutフィールドがありますが、それらを使用すると、tcp renoスタックの「tcp fast-path」が無効になります。

私が持っている唯一のアイデアは、ループでrecv(fd、...、MSG_DONTWAIT)を使用することです


スレッドを使用するオプションもあります:)ただし、スレッド信号は引き続き必要です
osgx

回答:


189

setsockopt関数を使用して、受信操作のタイムアウトを設定できます。

SO_RCVTIMEO

入力関数が完了するまで待機する最大時間を指定するタイムアウト値を設定します。入力操作の完了を待機する時間の制限を指定する秒数とマイクロ秒数のtimeval構造を受け入れます。追加のデータを受信せずに受信操作がこの時間ブロックされた場合、データが受信されない場合、部分カウントまたはerrnoを[EAGAIN]または[EWOULDBLOCK]に設定して戻ります。このオプションのデフォルトはゼロです。これは、受信操作がタイムアウトしないことを示します。このオプションは、timeval構造を取ります。すべての実装でこのオプションを設定できるわけではないことに注意してください。

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

Windowsでは、これはを呼び出す前に行う必要があると報告されていますbindbindLinuxとOS Xの前でも後でも実行できることを実験で確認しました。


1
この答えは私のお尻を救った。私はその複雑な「選択」がらくたの実装に行き詰まり、成功しませんでした。これはすぐに機能し、とても簡単になりました。
MiloDC 2017年

これがLinuxのコードを使用しているため、Windowsのタイムアウトが機能しない理由です。ありがとう。ウィンドウが使用されてstruct timeval tv;いない場合、それはselect()も機能しないことを意味しますか?select()コードをWindowsに移植してみましたが、すぐにタイムアウトになり、timevalで設定している値を無視しているように見えます。
kuchi

1
タイムアウト値を5秒に設定しました。着信データの有無にかかわらず、各読み取りサイクルに常に5秒かかるのはなぜですか?
ハン、

これは、バインド操作後でもウィンドウで機能します。Windows 10で試した
cahit beyaz 2018

1
@ user463035818 この答えは、そうすべきではないと主張しています。
Tomeamis

22

Cでrecv使用する関数にタイムアウトを追加する簡単なコードをいくつか次に示しますpoll

struct pollfd fd;
int ret;

fd.fd = mySocket; // your socket handler 
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
    case -1:
        // Error
        break;
    case 0:
        // Timeout 
        break;
    default:
        recv(mySocket,buf,sizeof(buf), 0); // get your data
        break;
}

これは期待どおりに機能しません。pollは、少なくとも1バイトまたはタイムアウトの受信recvを待機しsizeof(buf)ますが、関数を呼び出すと、バイトを待機するため、このカウントがまだ到着していない場合はタイムアウトしません。
LoPiTaL

0

// WINDOWSのバインド操作後にも機能します

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

-1

ハンドラをインストールSIGALRMし、使用しalarm()たりualarm()、通常のブロックの前にrecv()。アラームが鳴ると、recv()はにerrno設定されたエラーを返しますEINTR


8
アラーム(および信号)は、このタスクの間違った方法です。tcp高速パスを使用する場合は、レイテンシを最小限に抑える必要があります。信号が遅い。
osgx

2
@osgxタイムアウトが発生した場合にのみシグナルが発生します。
David Schwartz

-4

LINUX

struct timeval tv;
tv.tv_sec = 30;        // 30 Secs Timeout
tv.tv_usec = 0;        // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));

ウィンドウズ

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

bind()適切に実行するために、関数呼び出しの前にこの設定を行っています


4
この質問はすでに何年も前に回答されています。あなたのソリューションはどのような新しい価値をもたらしますか?
Maciej Jureczko 2017

この設定は、適切な実行のためにbind()関数呼び出しの前にこの設定を置いています。この部分はansに記載されていません
vivek
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.