参考:http://www.ibm.com/developerworks/cn/linux/l-hisock.html
开发socket程序有以下技巧:
- 最小化报文传输的延时。
- 最小化系统调用的负载。
- 为 Bandwidth Delay Product 调节 TCP 窗口。
- 动态优化 GNU/Linux TCP/IP 栈。
1 最小化报文传输的延时
1.1 背景
在通过 TCP socket 进行通信时,数据都拆分成了数据块,封装到给定连接的 TCP payload(指 TCP 数据包中的有效负荷)中。
TCP payload 的大小取决于几个因素(例如最大报文长度和路径),但是这些因素在连接发起时都是已知的。可能多的可用数据来填充每个报文。
当没有足够的数据来填充 payload 时(也称为最大报文段长度(maximum segment size) 或 MSS),TCP 就会采用 Nagle 算法自动将一些小的缓冲区连接到一个报文段中。
这样可以通过最小化所发送的报文的数量来提高应用程序的效率,并减轻整体的网络拥塞问题。
但有时,可能希望只发送一些较小的报文,没必要填充,比如 telnet 程序, HTTP协议。
1.2 Nagle 算法利弊:
A 对数据进行合并,试图构成一个完整的 TCP 报文段,可以最小化在线路上发送的报文的数量,因此可以最小化网络拥塞的问题。
B 会引入一些延时
1.3 提升
在需要最小化传输延时的情况中,可以禁用 Nagle 算法,通过设置 TCP_NODELAY
socket 选项
2 最小化系统调用的负载
2.1 背景
任何时候通过一个 socket 来读写数据时,您都是在使用一个系统调用(system call):跨越了用户空间应用程序与内核的边界。
另外,在进入内核之前,您的调用会通过 C 库来进入内核中的一个通用函数(system_call()
)。
从system_call()
中,这个调用会进入文件系统层,内核会在这儿确定正在处理的是哪种类型的设备。
最后,调用会进入 socket 层,数据就是在这里进行读取或进行排队从而通过 socket 进行传输的(这涉及数据的副本)。
2.2 影响:
因此,系统调用不仅仅是在应用程序和内核中进行操作的,而且还要经过应用程序和内核中的很多层次。
这个过程耗费的资源很高,因此调用次数越多,通过这个调用链进行的工作所需要的时间就越长,应用程序的性能也就越低。
虽然无法避免系统调用,但可以最小化使用这些调用的次数。
2.3 提升
在将数据写入一个 socket 时,尽量一次写入所有的数据,而不是执行多次写数据的操作。
对于读操作来说,最好传入可以支持的最大缓冲区,因为如果没有足够多的数据,内核也会试图填充整个缓冲区(另外还需要保持 TCP 的通告窗口为打开状态)。
3 为 Bandwidth Delay Product 调节 TCP 窗口
3.1 背景
TCP 的性能取决于两个最重要的因素:
链接带宽(link bandwidth)(报文在网络上传输的速率)
往返时间(round-trip time) 或 RTT(发送报文与接收到另一端的响应之间的延时)
这两个值确定了称为 Bandwidth Delay Product(BDP)的内容。给定链接带宽和 RTT 之后,就可以计算出 BDP 的值。
3.2 问题
BDP 给出了一种简单的方法来计算理论上最优的 TCP socket 缓冲区大小:
如果缓冲区太小,那么 TCP 窗口就不能完全打开,这会对性能造成限制;
如果缓冲区太大,那么宝贵的内存资源就会造成浪费。
所以,如果您设置的缓冲区大小正好合适,那么就可以完全利用可用的带宽。
下面我们来看一个例子:
BDP = link_bandwidth * RTT
如果应用程序是通过一个 100Mbps 的局域网进行通信,其 RRT 为 50 ms,那么 BDP 就是:
100MBps * 0.050 sec / 8 = 0.625MB = 625KB
因此,我们可以将 TCP 窗口设置为 BDP 或 1.25MB。但是在 Linux 2.6 上默认的 TCP 窗口大小是 110KB,这会将连接的带宽限制为 2.2MBps,计算方法如下:
throughput = window_size / RTT
110KB / 0.050 = 2.2MBps
如果使用上面计算的窗口大小,我们得到的带宽就是 12.5MBps,计算方法如下:
625KB / 0.050 = 12.5MBps
差别的确很大,并且可以为 socket 提供更大的吞吐量。因此现在您就知道如何为您的 socket 计算最优的缓冲区大小了。但是又该如何来改变呢?
3.3 提升
Sockets API 提供了几个 socket 选项,其中两个可以用于修改 socket 的发送和接收缓冲区的大小:SO_SNDBUF
和 SO_RCVBUF
在 Linux 2.6 内核中,发送缓冲区的大小是由调用用户来定义的,但是接收缓冲区会自动加倍。您可以进行 getsockopt
调用来验证每个缓冲区的大小。
对于window scaling,TCP最初可以支持最大为 64KB 的窗口(16位值定义窗口,采用 window scaling(RFC 1323)扩展后可用32位值表示)。
GNU/Linux 中提供的 TCP/IP 栈可以支持这个选项(以及其他一些选项)。
4 动态优化 GNU/Linux TCP/IP 栈
4.1 背景
标准的 GNU/Linux 发行版试图对各种部署情况都进行优化。这意味着标准的发行版可能并没有对您的环境进行特殊的优化。
4.2 提升
GNU/Linux 提供了很多可调节的内核参数,在 /proc
虚拟文件系统中存在一些可调节的内核参数。
这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 cat
工具进行读取,或使用 echo
命令进行修改。
清单 3 展示了如何查询或启用一个可调节的参数(在这种情况中,可以在 TCP/IP 栈中启用 IP 转发),如下表:
可调节的参数 | 默认值 | 选项说明 |
---|---|---|
/proc/sys/net/core/rmem_default | "110592" | 定义默认的接收窗口大小;对于更大的 BDP 来说,这个大小也应该更大。 |
/proc/sys/net/core/rmem_max | "110592" | 定义接收窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。 |
/proc/sys/net/core/wmem_default | "110592" | 定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。 |
/proc/sys/net/core/wmem_max | "110592" | 定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。 |
/proc/sys/net/ipv4/tcp_window_scaling | "1" | 启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。 |
/proc/sys/net/ipv4/tcp_sack | "1" | 启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用。 |
/proc/sys/net/ipv4/tcp_fack | "1" | 启用转发应答(Forward Acknowledgment),这可以进行有选择应答(SACK)从而减少拥塞情况的发生;这个选项也应该启用。 |
/proc/sys/net/ipv4/tcp_timestamps | "1" | 以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。 |
/proc/sys/net/ipv4/tcp_mem | "24576 32768 49152" | 确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。第一个值是内存使用的下限。第二个值是内存压力模式开始对缓冲区使用应用压力的上限。第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。 |
/proc/sys/net/ipv4/tcp_wmem | "4096 16384 131072" | 为自动调优定义每个 socket 使用的内存。第一个值是为 socket 的发送缓冲区分配的最少字节数。第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。 |
/proc/sys/net/ipv4/tcp_rmem | "4096 87380 174760" | 与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值。 |
/proc/sys/net/ipv4/tcp_low_latency | "0" | 允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项应该禁用。 |
/proc/sys/net/ipv4/tcp_westwood | "0" | 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。 |
/proc/sys/net/ipv4/tcp_bic | "1" | 为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。 |
附 GNU/Linux 工具
GNU/Linux 提供了几个工具:用于调试网络应用程序,测量带宽/吞吐量,以及检查链接的使用情况。
GNU/Linux 工具 | 用途 |
---|---|
ping | 这是用于检查主机的可用性的最常用的工具,但是也可以用于识别带宽延时产品计算的 RTT。 |
traceroute | 打印某个连接到网络主机所经过的包括一系列路由器和网关的路径(路由),从而确定每个 hop 之间的延时。 |
netstat | 确定有关网络子系统、协议和连接的各种统计信息。 |
tcpdump | 显示一个或多个连接的协议级的报文跟踪信息;其中还包括时间信息,您可以使用这些信息来研究不同协议服务的报文时间。 |
GNU/Linux 工具 | 用途 |
---|---|
netlog | 为应用程序提供一些有关网络性能方面的信息。 |
nettimer | 为瓶颈链接带宽生成一个度量标准;可以用于协议的自动优化。 |
Ethereal | 以一个易于使用的图形化界面提供了 tcpump (报文跟踪)的特性。 |
iperf | 测量 TCP 和 UDP 的网络性能;测量最大带宽,并汇报延时和数据报的丢失情况。 |