TCP/IP网络编程 -- (九)套接字的多种可选项
TCP/IP网络编程 -- (九)套接字的多种可选项
之前写的程序都是创建好 socket 后未经特殊设置直接使用,但有时需要更改
getsockopt & setsocket
可以对上述的可选项进行读取和设置操作(有些只能进行一种操作)
读取和设置根据以下两个函数完成
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
sock:用于查看 socket 的文件描述符
level:要查看的可选项的协议层
optname:要查看的可选项名
optval:保存查看结果的缓冲地址值
optlen:保存 optval 的字节数
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
参数与上述相同
state = getsocket(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
运行后 sock_type = 1,因为 SOCK_STREAM 的类型常数为 1
SO_SNDBUF & SO_RCVBUF
用于调整输出缓存与输入缓存的大小
读取 I/O 缓冲大小
state = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
设置 I/O 缓存
int rcv_buf = 1024 * 3;
state = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
Time-wait 状态
在第五章实现的 echo_server.c 与 echo_client.c 中,如果在 client 输入 Q 或者 ctrl + C 终止程序,那么 client 会向 server 发送 fin 来开始四次挥手过程,这种情况是没有问题的
但是如果在 server 端首先 ctrl + C 关闭连接,再次在同一端口启动 server 时会出现 "bind() error",等待三分钟后在同一端口启动可以正常运行
这是因为 TCP 四次挥手的过程中,主动断开连接的一方 A 会有一个 Time-wait 状态,是为了防止 A 发送的最后一次挥手对方没有收到,B 重传了 fin,而这时连接已经中断了
但是如果客户端首先发起断开请求则不会出现该情况,因为客户端不是指定端口号而是自动分配,不会短时间内重复分配相同端口
地址再分配
Time-wait 也有缺点,如果系统发生故障紧急停止,下次重连时还要等待几分钟,甚至在第四次挥手丢失时还会重启 Time-wait,时间会更长
解决方案是在 socket 可选项中更改 SO_REUSEADDR 状态,如果 SO_REUSEADDR 设为 1 则表示新的 socket 可以使用正在 Time-wait 阶段的端口号,为 0 则不可使用(默认情况)
optlen = sizeof(option);
option = true;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen);
9.3TCP_NODELAY
Nagle 算法
为了防止网络中的数据包太多发生网络过载,发明了 Nagle 算法,如果设包含很少有效载荷的数据包为小包,载荷大小为一个 MSS 的是大包,核心是
网络中的数据包尽可能以大包的形式发送
只有收到前一个数据的 ACK 时,Nagle 算法才发送下一个数据,但是已经先进入到输出缓冲中
但不是什么情况下使用 Nagle 都更好,如果传输大文件时不使用 Nagle 会更好,因为即使不使用,网络中大部分也是大包,反而无需等待 ACK 就可流水线地发送数据包
禁用 Nagle 算法
将 TCP_NODELAY 改为 1 即可
int opt_val = 1;
setsocket(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&opt_val, sizeof(opt_val));