前段时间研究分布式时写了一个可扩展的服务器组程序,服务器组之间通信时老是达不到想要的性能。后来抓包排查,原来是TCP滑动窗口引起的问题,本来是很基础的东西,奈何当初没有太在意,导致错误的产生,现在详细写出来,忘不太清楚者警惕!
滑动窗口的基本情况我有必要废话一下。TCP通信为了保证可靠性,每次发送的数据都需要得到对方的ACK才确认对方收到了(仅保证对方TCP接收缓冲收到数据了,但不保证对方应用程序取到数据了),这时如果每次发送一次就要停下来等着对方的ACK消息,显然是一种极大的资源浪费和低下的效率,这时就有了滑动窗口的出现。
发送方的滑动窗口维持着当前发送的帧序号,已发出去帧的计时器,接收方当前的窗口大小(由接收方ACK通知,大体等于接收缓冲大小-未处理的消息包),接收方滑动窗口保存的有已接收的帧信息、期待的下一帧的帧号等,至于滑动窗口的具体工作原理这里就不说了。
一个socket有两个滑动窗口(一个sendbuf、一个recvbuf),两个窗口的大小是通过setsockopt函数设置的,现在问题就出在这里,通过抓包显示,设置的窗口大小没有生效,最后排查发现setsockopt函数是后来加上的,写到了listen函数的后面,这样每次accept出的socket并没有继承得到主socket设置的窗口大小,无语啊……
解决办法:setsockopt函数提前到listen函数之前,这样在服务器程序启动监听前recvbuf就已经有了,accept后的链接得到的就是recvbuf了,启动程序运行,抓包显示窗口已经是指定的大小了。
网络编程其实很简单,任何人都可以写出一套自己的服务器框架,但是细节决定成败,性能的高低有时候就是几个小细节决定的(当然这里说的这个问题是个编程错误,不属于可优化的细节问题)