在研究 pcp_child.c 中的代码的时候,看到 pcp_do_accept 函数中有如下代码:
if (setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)) < 0) { pool_error("pcp_child: setsockopt() failed: %s", strerror(errno)); close(afd); return NULL; }
Google了一下,发现是:为了最小化 报文传输的延迟。就是说不会对报文合并,有了数据就进行发送。
但是下一个是比较奇怪的:
if (setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) { pool_error("pcp_child: setsockopt() failed: %s", strerror(errno)); close(afd); return NULL; }
Google了一下,人家都是这么说的:
设置 SO_KEEPALIVE 选项 SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。 设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。 这是一个对方必须响应的TCP分节. 它会导致以下三种情况: 对方接收一切正常:以期望的ACK响应。 2小时后,TCP将发出另一个探测分节。 对方已崩溃且已重新启动: 以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭。 对方无任何响应: 源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。 在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。 如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。在该书的第158页有更详细的描述。 根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候, 我们可以设置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。 keepAlive = 1; Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive)); 如果我们不能接受如此之长的等待时间, 从TCP-Keepalive-HOWTO上可以知道一共有两种方式可以设置, 一种是修改内核关于网络方面的配置参数, 另外一种就是SOL_TCP字段的TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT三个选项。
英文说明在:http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/programming.html
但是我的问题在于:pcp_pool_status等各种客户端命令,都是立即结束的。根本就不会维持和服务器端的长连接。经过试验,修改 下列参数:
/proc/sys/net/ipv4/tcp_keepalive_time
/proc/sys/net/ipv4/tcp_keepalive_intvl
/proc/sys/net/ipv4/tcp_keepalive_probes
改为 tcp_keepalive_time=180秒 tcp_keepalive_intvl=10秒 tcp_keepalive_probes=6个
再次运行,无论程序中是否包含
if (setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) {
pool_error("pcp_child: setsockopt() failed: %s", strerror(errno));
close(afd);
return NULL;
}
都可以正常多次运行 pcp_pool_status 命令,后台产生响应的 socket描述符的值也都一样。
怀疑当初的开发者考虑过长连接的问题:就是pcp_pool_status 之类不断运行,长连接并不断开。但最终pcp客户端并没有如此实现。