Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

TCP常用socketopt

简介

开一篇文章来专门分析TCP常用的参数设定。

libevent提供的修改选项

libevent关于tcp的选项主要放到evutil.c文件中。

关键函数

fcntl

Linux里面使用fcntl可以对socket这个fd做一些查询调整。

O_NONBLOCK

提供了一个设置非阻塞方式的fd。

1
2
3
4
5
6
7
8
int flags;
if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) { // 抓取fd上全部的设置位
event_warn("fcntl(%d, F_GETFL)", fd);
return -1;
}
if (!(flags & O_NONBLOCK)) { // 如果非阻塞方式
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { // 将非阻塞方式打开
event_warn("fcntl(%d, F_SETFL)", fd);
FD_CLOEXEC

当执行成功之后将会自动释放掉fd。

If the FD_CLOEXEC bit is set, the file descriptor will automatically be closed during a successful execve(2). (If the execve(2) fails, the file descriptor is left open.) If he FD_CLOEXEC bit is not set, the file descriptor will remain open across an execve(2).

如果FD_CLOEXEC位置设置了,这个文件描述字将会自动被关闭在execve(2)函数执行成功时候。(如果execve(2)失败了,文件描述字将继续开着。)如果FD_CLOEXEC没有被设置,文件描述字将会保持打开状态即使执行了execve(2)

  • 在创建socket的时候,也会指定EVUTIL_SOCK_CLOEXEC
1
2
3
4
5
6
7
8
9
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
close(fd);

execve - execute program
##include <unistd.h>

int execve(const char *filename, char *const argv[],
char *const envp[]);
execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting.
setsockopt
SO_REUSEADDR

原文注释:

REUSEADDR on Unix means, “don’t hang on to this address after the listener is closed.” On Windows, though, it means “don’t keep other processes from binding to this address while we’re using it.”

REUSEADDR 在Unix的意义,“不要挂起地址,当listener已经被关闭。”然而,在Windows,它的意义,“不用拒绝别的进程绑定此地址,当我们已经使用了它。”。

所以这个选项只在非Windows环境开启。

SO_REUSEPORT

原文注释:

REUSEPORT on Linux 3.9+ means, “Multiple servers (processes or threads) can bind to the same port if they each set the option.

在Linux 3.9+ 内核版本的意义,“多服务(进程或线程)能绑定相同端口,如果它们都设置了这个选项。”

IPV6_V6ONLY

将socket设置成只支持IPV6。

1
2
3
int one = 1;
return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*) &one,
(ev_socklen_t)sizeof(one));
TCP_DEFER_ACCEPT

原文注释:

TCP_DEFER_ACCEPT tells the kernel to call defer accept() only after data has arrived and ready to read

告诉内核延时调用accept(),只有当其数据已经到达而且准备好读取时。

SO_KEEPALIVE

listenerhttp当成accept的socket才做了这个设定。

Enable sending of keep-alive messages on connection-oriented sockets. Expects an integer boolean flag.

允许发送keep-alive消息在面向连接的sockets上。

1
2
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on))<0)
SO_LINGER

LINGER:持续观察。

在libevent源码中,只有在benchmark测试代码里面用了一下。在平时游戏服务器里面是不要用这个开关,否则会让服务器直接卡死。

手册原文:

When enabled, a close(2) or shutdown(2) will not return until all queued messages for the socket have been successfully sent or the linger timeout has been reached. Otherwise, the call returns immediately and the closing is done in the background. When the socket is closed as part of exit(2), it always lingers in the background.

生效时,一个close(2)或者shutdown(2)函数将不会返回直到属于socket全部队列数据都已经发送成功或持续观察超时时间已经到了。否则,调用之后将会立即返回而且关闭动作将会在后台继续执行。当socket被关闭通过exit(2),它也将持续观察在后台。

SO_RCVBUF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sets or gets the maximum socket receive buffer in bytes.
The kernel doubles this value (to allow space for
bookkeeping overhead) when it is set using setsockopt(2),
and this doubled value is returned by getsockopt(2). The
default value is set by the
/proc/sys/net/core/rmem_default file, and the maximum
allowed value is set by the /proc/sys/net/core/rmem_max
file. The minimum (doubled) value for this option is 256.

设置或者获取socket接受buffer的字节数。
当通过setsockopt函数设置,内核将放大一倍(为记账留出空间)。
此默认值被记录在 /proc/sys/net/core/rmem_default 文件中,
最大值记录在 /proc/sys/net/core/rmem_max 文件中。最小值(双倍)
被设定为256。
SO_SNDBUF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Sets or gets the maximum socket send buffer in bytes.  The
kernel doubles this value (to allow space for bookkeeping
overhead) when it is set using setsockopt(2), and this
doubled value is returned by getsockopt(2). The default
value is set by the /proc/sys/net/core/wmem_default file
and the maximum allowed value is set by the
/proc/sys/net/core/wmem_max file. The minimum (doubled)
value for this option is 2048.

设置或者获取socket发送buffer的字节数。
当通过setsockopt函数设置,内核将放大一倍(为记账留出空间)。
此默认值被记录在 /proc/sys/net/core/wmem_default 文件中,
最大值记录在 /proc/sys/net/core/wmem_max 文件中。最小值(双倍)
被设定为2048。

dogecoin源码中如何处理

TCP_NODELAY

关闭了Nagle算法。这个在libevent里面没有提供。

Disable Nagle’s algorithm.

SO_REUSEADDR

listener已经关闭之后不要挂起这个地址。

Allow binding if the port is still in TIME_WAIT state after the program was closed and restarted.

允许绑定如果这个端口依然处在TIME_WAIT状态,当程序被关闭或者重启了。

IPV6_V6ONLY

开启IPV6的socket。

SO_NOSIGPIPE

1
2
3
4
Different way of disabling SIGPIPE on BSD
在BSD系统中关闭掉SIGPIPE
A SIGPIPE is sent to a process if it tried to write to a socket that had been shutdown for writing or isn't connected (anymore).
A SIGPIPE(信号管道)发送给一个进程如果它尝试去写一个不能连接或者断开连接失败的情况。

libuv源码

大量的和libevent相同。

SO_OOBINLINE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
##if defined(__APPLE__)
enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) &&
errno != ENOTSOCK &&
errno != EINVAL) {
return UV__ERR(errno);

/**
* When the SO_OOBINLINE socket
* option is enabled, urgent data is put into the normal data stream
* (a program can test for its location using the SIOCATMARK ioctl
* described below), otherwise it can be received only when the
* MSG_OOB flag is set for recv(2) or recvmsg(2).
* 当SO_OOBINLINE设置了,紧急的数据将会放入到data stream里面,否则将会在 recv
* recvmsg 函数 flag MSG_OOB 开启下收到这些紧急通知信息。
*/

TCP_KEEPIDLE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
##ifdef TCP_KEEPIDLE
if (on) {
int intvl = 1; /* 1 second; same as default on Win32 */
int cnt = 10; /* 10 retries; same as hardcoded on Win32 */
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
return UV__ERR(errno);
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
return UV__ERR(errno);
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
return UV__ERR(errno);
}
##endif

// * 这个选项不应该被使用在试图可以移植的代码中。这三个参数移植性比较差。
//
// TCP_KEEPCNT (since Linux 2.4)
// The maximum number of keepalive probes TCP should send
// before dropping the connection. This option should not be
// used in code intended to be portable.
// TCP_KEEPCNT 最大TCP发送探测次数,在丢失掉了连接之后。
//
// TCP_KEEPIDLE (since Linux 2.4)
// The time (in seconds) the connection needs to remain idle
// before TCP starts sending keepalive probes, if the socket
// option SO_KEEPALIVE has been set on this socket. This
// option should not be used in code intended to be portable.
// TCP_KEEPIDLE 多久(单位秒)链接需要停留待机,之前发送过保活探测,在这个
// 套接字开启了保活选项前提下。
//
// TCP_KEEPINTVL (since Linux 2.4)
// The time (in seconds) between individual keepalive probes.
// This option should not be used in code intended to be
// portable.
// 多久(单位秒)两次单独保活探测时间。

IP_MULTICAST_IF

用于UDP广播。

1
2
3
4
5
6
if (addr_st.ss_family == AF_INET) {
if (setsockopt(handle->io_watcher.fd,
IPPROTO_IP,
IP_MULTICAST_IF,
(void*) &addr4->sin_addr,
sizeof(addr4->sin_addr)) == -1)

引用