1 はじめに
SO_SNDBUF,SO_RCVBUFは、ソケットの送受信バッファの最大サイズの設定、取得をするソケットオプションです。ここでは、SO_SNDBUFについて説明をします。
オプション | 意味 |
---|---|
SO_SNDBUF | ソケットの送信バッファーの最大サイズを設定、取得をする |
SO_RCVBUF | ソケットの受信バッファーの最大サイズを設定、取得をする |
その他ソケットオプションについても記事を書きました。
ソケットオプションの使い方(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 ソースコードの説明
送信バッファの最大サイズは、以下のsock_setsockopt関数で設定します。sock_setsockopt関数はsetsockoptシステムコールの延長で呼び出すカーネル関数です。第4引数(optval)は、ソケットオプションによって意味が異なります。SO_SNDBUFソケットオプションの場合、送信バッファサイズの格納領域へのポインタになります。
846 int sock_setsockopt(struct socket *sock, int level, int optname, 847 char __user *optval, unsigned int optlen) -snip- 866 if (get_user(val, (int __user *)optval)) 867 return -EFAULT; -snip- 899 case SO_SNDBUF: 900 /* Don't error on this BSD doesn't and if you think 901 * about it this is right. Otherwise apps have to 902 * play 'guess the biggest size' games. RCVBUF/SNDBUF 903 * are treated in BSD as hints 904 */ 905 val = min_t(u32, val, sysctl_wmem_max); 906 set_sndbuf: 907 /* Ensure val * 2 fits into an int, to prevent max_t() 908 * from treating it as a negative value. 909 */ 910 val = min_t(int, val, INT_MAX / 2); 911 sk->sk_userlocks |= SOCK_SNDBUF_LOCK; 912 sk->sk_sndbuf = max_t(int, val * 2, SOCK_MIN_SNDBUF); 913 /* Wake up sending tasks if we upped the value. */ 914 sk->sk_write_space(sk); 915 break;
ソースコードの変数について説明をします。
変数,定数 | 意味 |
---|---|
val | 送信バッファサイズを保存する変数です。866行目でソケット利用者が指定した送信バッファサイズをvalに保存しています |
sysctl_wmem_max | システムで利用できる送信バッファの最大サイズを保存するカーネルパラメータ(net.core.wmem_max)です。私の環境では、この値は212992でした。905行目を確認すると、ソケット利用者が指定できる送信バッファサイズの最大値は、カーネルパラメータの値より小さくなることがわかります |
sk_sndbuf | 送信バッファの最大サイズを保存する変数です。912行目で送信バッファの最大サイズをsk_sndbufに保存しています |
SOCK_MIN_SNDBUF | 4608バイトの固定値です |
ソースコードを確認すると、ソケット利用者が指定する送信バッファの最大サイズ(val)とカーネルが割り当てる送信バッファの最大サイズ(sk_sndbuf)は以下の関係になることがわかります。
簡単にグラフが描けるツールが見つからなかったのでwindowsのペイントを使いました。いいツールがあったら教えてください。
3 検証環境
3.1 ネットワーク構成
サーバとクライアントの2台構成です。図中のeth0はNICの名前です。
192.168.122.0/24 client(eth0) ----------------------------------------------------(eth0) server .177 .68
3.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
4 事前準備
クライアントからサーバの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
5 テストプログラム(以降TP)の作成
TPを作成します。なお、エラー処理は見やすくするため意図的に省略しています。また、ソケットオプションの動作確認を目的としているため、実用的なプログラムにはなっていません。
・サーバ側:ncコマンドを使います。なお、ncコマンドのインストール方法、使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。
・クライアント側:以下のTPをコンパイルして使用します。TPはデフォルトの送信バッファの最大サイズを表示したあと、ソケット利用者が指定した送信バッファの最大サイズを設定します。そして、設定した送信バッファの最大サイズを表示します。TPの第1引数には、送信バッファの最大サイズを指定します。なお、見やすくするため、不要な異常処理は意図的に省略しています。
[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, sendbuff; struct sockaddr_in server; socklen_t optlen; 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"); connect(sock, (struct sockaddr *)&server, sizeof(server)); optlen = sizeof(sendbuff); getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendbuff, (socklen_t *)&optlen); printf("send buffer size(before) = %d\n",sendbuff); sendbuff = atoi(argv[1]); setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendbuff, (socklen_t )optlen); getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void *)&sendbuff, (socklen_t *)&optlen); printf("send buffer size(after) = %d\n",sendbuff); close(sock); return 0; }
TPをコンパイルします。
[root@client ~]# gcc -Wall -o cl cl.c
6 検証結果
TPで指定する送信バッファサイズを変化させて、そのとき、カーネルが割り当てる送信バッファサイズが冒頭で記述したグラフのようになるかを検証してみたいと思います。
サーバでncコマンドを実行します。
[root@server ~]# nc -kl 11111
クライアントでTPを実行します。このとき、送信バッファの最大サイズとして2303を指定します。実行結果を確認すると、カーネルが割り当てた送信バッファの最大サイズは4608であることがわかります。
[root@client ~]# ./cl 2303 send buffer size(before) = 87040 send buffer size(after) = 4608
送信バッファの最大サイズとして2304を指定すると、カーネルが割り当てた送信バッファの最大サイズは4608であることがわかります。
[root@client ~]# ./cl 2304 send buffer size(before) = 87040 send buffer size(after) = 4608
送信バッファの最大サイズとして2305を指定すると、カーネルが割り当てた送信バッファの最大サイズは4610であることがわかります。
[root@client ~]# ./cl 2305 send buffer size(before) = 87040 send buffer size(after) = 4610
以降、実行結果を記載しておきます。
[root@client ~]# ./cl 2306 send buffer size(before) = 87040 send buffer size(after) = 4612
[root@client ~]# ./cl 212990 send buffer size(before) = 87040 send buffer size(after) = 425980
[root@client ~]# ./cl 212991 send buffer size(before) = 87040 send buffer size(after) = 425982
[root@client ~]# ./cl 212992 send buffer size(before) = 87040 send buffer size(after) = 425984
[root@client ~]# ./cl 212993 send buffer size(before) = 87040 send buffer size(after) = 425984
Z 参考情報
私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ