1 SO_RCVTIMEOとは?
SO_RCVTIMEOは、受信系システムコール(read, recv, recvfrom, recvmsg)に対して使用するソケットオプションです。これらシステムコールのデフォルト動作はブロッキングモードです。したがって、ソケットの受信バッファに受信データが届くまで、システムコールの実行が完了しません(ブロックしたままになります)。しかし、SO_RCVTIMEOを使うと、データを受信しなくても、指定した時間でシステムコールの実行を完了させることができます。
その他ソケットオプションについても記事を書きました。
ソケットオプションの使い方(SO_REUSEPORT) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(TCP_NODELAY編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(TCP_CORK編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_REUSEADDR編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_SNDBUF編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_SNDTIMEO編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_RCVTIMEO編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_KEEPALIVE編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(TCP_NODELAY編) - hana_shinのLinux技術ブログ
ソケットオプションの使い方(SO_LINGER編) - hana_shinのLinux技術ブログ
2 検証環境
2.1 ネットワーク構成
サーバとクライアントの2台構成です。図中のenp1s0はNICの名前です。
192.168.122.0/24 client(enp1s0) ------------------------------------------(enp1s0) server .177 .68
2.2 版数
サーバとクライアントのAlmaLinuxの版数は以下のとおりです。
[root@server ~]# cat /etc/redhat-release AlmaLinux release 8.6 (Sky Tiger)
カーネル版数は以下のとおりです。
[root@server ~]# uname -r 4.18.0-372.9.1.el8.x86_64
クライアントからサーバの11111番ポートにアクセスできるようにするため、TCPの11111番ポートを解放します。なお、firewall-cmdの使い方は、firewall-cmdの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。
[root@server ~]# firewall-cmd --add-port=11111/tcp success
ルールを確認します。11111番ポートへのアクセスを許可するルールが追加されたことがわかります。
[root@server ~]# firewall-cmd --list-ports 11111/tcp
3 テストプログラム(以降TP)の作成
サーバ、クライアントで実行するTPを作成します。ソースコードを見やすくするため、意図的に異常処理は省略しています。また、ソケットオプションの動作確認を目的としているため、実用的なプログラムにはなっていません。
・サーバ側:ncコマンドを使います。なお、ncコマンドのインストール方法、使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。
・クライアント側:以下のTPをコンパイルして使用します。なお、コードを見やすくするため、異常処理は意図的に省略しています。
[root@client ~]# cat cl.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { int sock; struct sockaddr_in server; struct timeval timeout; char buf[4096]; sock = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_port = htons(11111); server.sin_addr.s_addr = inet_addr("192.168.122.68"); if(atoi(argv[1]) != 0) { timeout.tv_sec =atoi(argv[1]); timeout.tv_usec = 0; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout); } connect(sock, (struct sockaddr *)&server, sizeof(server)); read(sock, buf, sizeof(buf)); perror("read"); close(sock); return 0; }
TPの第1引数の意味は次のとおりです。
第1引数 | 意味 |
---|---|
0 | ソケットの受信バッファにデータが到着するまで、システムコールの実行は完了しません(ブロックしたままになります) |
0以外 | システムコールの実行時間を指定します。ソケットの受信バッファにデータが到着しなくても、指定した時間でシステムコールの実行が完了します |
TPをコンパイルします。
[root@client ~]# gcc -Wall -o cl cl.c
4 動作確認
サー側でncコマンドを実行します。
[root@server ~]# nc -kl 11111
4.1 TPの引数に1を指定した場合
readシステムコールの実行時間を確認するため、straceコマンドを使用します。straceの実行結果(3行目の右端)を確認すると、readシステムコールの実行時間が1.01秒であることがわかります。TPの引数に指定した1秒におおよそ一致していることがわかります。なお、straceコマンドの使い方は、https://hana-shin.hatenablog.com/entry/2021/12/25/215551:titileを参照してください。
[root@client ~]# strace -ttT -e trace=read ./cl 1 20:25:27.760788 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\255\3\0\0\0\0\0"..., 832) = 832 <0.000267> 20:25:27.765834 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 <0.000912> 20:25:27.784649 read(3, 0x7ffcee8e6620, 4096) = -1 EAGAIN (リソースが一時的に利用できません) <1.010118> read: Resource temporarily unavailable 20:25:28.805899 +++ exited with 0 +++
4.2 TPの引数に2を指定した場合
次に、TPの引数に2秒を指定してみます。straceの実行結果(3行目の右端)を確認すると、readシステムコールの実行時間が2.02秒であることがわかります。TPの引数に指定した2秒におおよそ一致していることがわかります。
[root@client ~]# strace -ttT -e trace=read ./cl 2 20:31:55.437992 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\255\3\0\0\0\0\0"..., 832) = 832 <0.000313> 20:31:55.440177 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 <0.000200> 20:31:55.447935 read(3, 0x7ffeda0dc200, 4096) = -1 EAGAIN (リソースが一時的に利用できません) <2.018727> read: Resource temporarily unavailable 20:31:57.481223 +++ exited with 0 +++
4.3 TPの引数に0を指定した場合
最後に、TPの引数に0秒を指定してみます。引数に0を指定すると、システムコールの実行が完了しないことがわかります。
[root@client ~]# strace -ttT -e trace=read ./cl 0 20:32:24.817061 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\255\3\0\0\0\0\0"..., 832) = 832 <0.000425> 20:32:24.823672 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32 <0.000666> 20:32:24.840294 read(3,
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ