昨天自己在测试端口是否能链接,发现下载服务器链接后,不会在制定的时间内被踢下线。觉得是个bug,决定调试一下,看了一下Jovi下载服务的代码,里面有相应的超时处理,觉得有点怪,继续检查日志,发现日志里面完全没有Accept处理的过程。简单叙述症状就是客户端Telnet成功,服务器端没有任何相应。
于是开始怀疑防火墙,换IP,换地点测试了半天无果。还是Sonicmao找到了问题原因。
Jovi的代码上有这样一段:
#ifndef WIN32
//TCP_DEFER_ACCEPT
int val = 1;
ret = peer_acceptor_.set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
ACE_DEBUG((LM_INFO," set_option TCP_DEFER_ACCEPT val(%d) ret(%d). /n", val, ret));
#endif
查询资料,TCP_DEFER_ACCEPT是一个很有趣的选项,
Linux 提供的一个特殊 setsockopt , 在 accept 的 socket 上面,只有当实际收到了数据,才唤醒正在accept 的进程,可以减少一些无聊的上下文切换。代码如下。
val = 5;
setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)) ;
里面 val 的单位是秒,注意如果打开这个功能,kernel 在 val 秒之内还没有收到数据,不会继续唤醒进程,而是直接丢弃连接。
经过测试发现,设置TCP_DEFER_ACCEPT选项后,服务器受到一个CONNECT请求后,操作系统不会Accept,也不会创建IO句柄。操作系统应该在若干秒,(但肯定远远大于上面设置的1s) 后,会释放相关的链接。但没有同时关闭相应的端口,所以客户端会一直以为处于链接状态。如果Connect后面马上有后续的发送数据,那么服务器会调用Accept接收这个链接端口。
感觉了一下,这个端口设置对于CONNECT链接上来而又什么都不干的攻击方式处理很有效。我们原来的代码都是先允许链接,然后再进行超时处理,比他这个有点Out了。不过这个选项可能会导致定位某些问题麻烦