TCP套接字连接是否"保持活动"?

Kev*_*oyd 77 java sockets tcp http keep-alive

我听说过HTTP keep-alive但是现在我想打开一个与远程服务器的套接字连接.
现在这个套接字连接是否会永远保持打开状态,或者是否存在与之相关的超时限制,类似于HTTP keep-alive?

Cor*_*ein 82

现在这个套接字连接是否会永远保持打开状态,或者是否存在与之相关的超时限制,类似于HTTP keep-alive?

简短的回答是,是的,有一个超时,它通过TCP Keep-Alive强制执行.

如果要配置保持活动超时,请参阅下面的"更改TCP超时"部分.

介绍

TCP连接由两个插槽组成,每个插槽位于连接的两端.当一方想要终止连接时,它会发送RST另一方确认的数据包并关闭其套接字.

然而,在此之前,双方将无限期地保持其套接字开放.这使得一方可能有意或由于某些错误而关闭其插座,而无需通知另一端RST.为了检测此场景并关闭过时连接,使用TCP Keep Alive进程.

保持活力过程

有三个可配置属性可确定Keep-Alives的工作方式.在Linux上他们是1:

  • tcp_keepalive_time
    • 默认7200秒
  • tcp_keepalive_probes
    • 默认9
  • tcp_keepalive_intvl
    • 默认75秒

这个过程是这样的:

  1. 客户端打开TCP连接
  2. 如果连接静默tcp_keepalive_time几秒钟,则发送一个空ACK数据包.1
  3. 服务器是否以ACK自己的相应方式响应?
    • 没有
      1. tcp_keepalive_intvl几秒钟,然后发送另一个ACK
      2. 重复,直到ACK已发送的探测数等于tcp_keepalive_probes.
      3. 如果此时未收到响应,请发送RST并终止连接.
    • :返回第2步

默认情况下,在大多数操作系统上启用此过程,因此一旦另一端无响应2小时11分钟(7200秒+ 75*9秒),则会定期修剪死TCP连接.

陷阱

2小时默认值

由于默认情况下连接空闲两小时后才开始进程,因此过时的TCP连接可能会在修剪之前停留很长时间.这对于诸如数据库连接之类的昂贵连接尤其有害.

Keep-Alive是可选的

根据RFC 1122 4.2.3.6,响应和/或中继TCP Keep-Alive数据包是可选的:

实现者可以在他们的TCP实现中包含"keep-alives",尽管这种做法并不是普遍接受的.如果包含keep-alives,应用程序必须能够为每个TCP连接打开或关闭它们,并且它们必须默认为关闭.

...

记住TCP不能可靠地传输不包含数据的ACK段是非常重要的.

原因是Keep-Alive数据包不包含任何数据,并且不是严格必要的,如果过度使用,可能会堵塞互联网的管道.

然而,在实践中,我的经验是,随着带宽变得越来越便宜,这种担忧随着时间的推移逐渐减少; 因此,通常不会丢弃Keep-Alive数据包.例如,亚马逊EC2文档间接认可Keep-Alive,因此如果您使用AWS托管,您可能会安全地依赖Keep-Alive,但您的里程可能会有所不同.

更改TCP超时

每个插座

不幸的是,由于TCP连接是在操作系统级别上进行管理的,因此Java不支持在每个套接字级别上配置超时,例如java.net.Socket.我发现了一些尝试3使用Java本机接口(JNI)来创建调用本机代码来配置这些选项的Java套接字,但似乎没有一个广泛的社区采用或支持.

相反,您可能被迫将配置作为一个整体应用于操作系统.请注意,此配置将影响整个系统上运行的所有TCP连接.

Linux的

可以在中找到当前配置的TCP Keep-Alive设置

  • /proc/sys/net/ipv4/tcp_keepalive_time
  • /proc/sys/net/ipv4/tcp_keepalive_probes
  • /proc/sys/net/ipv4/tcp_keepalive_intvl

您可以更新以下任何内容:

# Send first Keep-Alive packet when a TCP socket has been idle for 3 minutes
$ echo 180 > /proc/sys/net/ipv4/tcp_keepalive_time
# Send three Keep-Alive probes...
$ echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
# ... spaced 10 seconds apart.
$ echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl
Run Code Online (Sandbox Code Playgroud)

这种变化不会在重启后持续存在.要进行持久更改,请使用sysctl:

sysctl -w net.ipv4.tcp_keepalive_time=180 net.ipv4.tcp_keepalive_probes=3 net.ipv4.tcp_keepalive_intvl=10
Run Code Online (Sandbox Code Playgroud)

Mac OS X.

可以使用以下方式查看当前配置的设置sysctl:

$ sysctl net.inet.tcp | grep -E "keepidle|keepintvl|keepcnt"
net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.keepcnt: 8
Run Code Online (Sandbox Code Playgroud)

值得注意的是,Mac OS X定义keepidlekeepintvl以毫秒为单位,而不是使用秒的Linux.

可以设置属性,sysctl以便在重新启动后保留这些设置:

sysctl -w net.inet.tcp.keepidle=180000 net.inet.tcp.keepcnt=3 net.inet.tcp.keepintvl=10000
Run Code Online (Sandbox Code Playgroud)

或者,您可以将它们添加到/etc/sysctl.conf(如果文件不存在,则创建该文件).

$ cat /etc/sysctl.conf
net.inet.tcp.keepidle=180000
net.inet.tcp.keepintvl=10000
net.inet.tcp.keepcnt=3
Run Code Online (Sandbox Code Playgroud)

视窗

我没有Windows机器来确认,但您应该在注册表中找到相应的TCP Keep-Alive设置

\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\TCPIP\Parameters

脚注

1.有关man tcp更多信息,请参阅.

2.此数据包通常称为"保持活动"数据包,但在TCP规范中,它只是一个常规ACK数据包.像Wireshark这样的应用程序能够通过元数据分析将其标记为"保持活动"数据包,它包含参考插槽上先前通信所包含的序列和确认号.

我从谷歌基本搜索中找到的一些例子是lucwilliams/JavaLinuxNetflonatel/libdontdie.

  • 非常有帮助,谢谢!一项补充:对于 Windows,需要重新启动才能使新的 KeepAliveTime 值生效。 (2认同)

Mat*_*ley 67

TCP套接字保持打开状态,直到它们关闭.

也就是说,在没有实际发送数据的情况下检测断开连接(断路,如路由器死亡等,而非关闭)非常困难,因此大多数应用程序每隔一段时间就会做一些ping/pong反应,以确保连接仍然存在.

  • @Tim在应用程序级别保持活动的原因是TCP标准建议将保持活动计时器设置为超过两个小时.从未见过没有流量的TCP连接.因此,TCP保持活动的东西默认是无用的. (13认同)
  • @TimCooper:这不是真的.正如我在其他答案的评论中强调的那样,TCP实现对大多数*应用程序*级别要求没有用.您无法按需发送一个,并且对于大多数操作系统,TCP keepalive超时只能在系统级别上配置,并且设置得太高而不能通常对应用程序有用. (7认同)
  • 这是个好主意.你没有*,但如果你没有,那么你可能不会发现一个断开的链接,直到有人真的想做某事.取决于你实际想要达到的目标,这可能是也可能不是好事(或可能或可能不重要). (4认同)
  • @MatthewScharley:这个"乒乓"已经在标准的TCP实现中为我们实现了,并被称为"keep-alive"(参见这个问题的另一个流行的答案).有没有理由在应用程序级别实现它? (4认同)

Ste*_*n C 53

您正在寻找SO_KEEPALIVE套接字选项.

的Java的Socket API暴露了"保活"通过应用程序setKeepAlivegetKeepAlive方法.

编辑:SO_KEEPALIVE在OS网络协议栈中实现,不发送任何"真实"数据.保持活动间隔取决于操作系统,并且可以通过内核参数进行调整.

由于没有数据发送,SO_KEEPALIVE只能测试网络连接的活跃程度,而不能测试套接字所连接的服务的活跃程度.要测试后者,您需要实现一些涉及向服务器发送消息并获得响应的内容.

  • 如果我是setKeepAlive(true); 什么是间隔?... Java也会在默认的时间间隔内发送保持活动的消息,还是我必须以编程方式执行此操作? (4认同)
  • @MatthewScharley关于"它不能**默认**至少不超过两小时"......意味着它可以不到两个小时吗? (4认同)
  • http://www.unixguide.net/network/socketfaq/4.7.shtml有SO_KEEPALIVE的描述.这并不是OP想要的,尽管它*是*我建议的基于协议的选项......但是,每两个小时一次对应用程序不会做太多. (3认同)
  • @Daniel-替代方法(在Java中)是*手动*保持活动,如上所述和其他答案。虽然不漂亮,但它可能比操作系统范围内的默认更改(可能会破坏系统服务或其他应用程序)更好。 (2认同)

ZZ *_*der 33

TCP keepalive和HTTP keepalive是非常不同的概念.在TCP中,keepalive是为检测过时连接而发送的管理数据包.在HTTP中,keepalive表示持久连接状态.

这是来自TCP规范,

只有在间隔内没有收到连接的数据或确认数据包时,才能发送保持活动的数据包.这个间隔必须是可配置的,必须默认不少于两个小时.

如您所见,对于大多数应用程序,默认TCP keepalive间隔太长.您可能必须在应用程序协议中添加keepalive.

  • 您可以修改TCP keepalive间隔以适合您的应用程序.例如http://msdn.microsoft.com/en-us/library/dd877220%28VS.85%29.aspx (2认同)

Art*_*ius 23

如果你是伪装NAT的背后(如今大多数家庭用户都是这样),那么外部端口池有限,这些端口必须在TCP连接之间共享.因此,伪装NAT倾向于假设如果在特定时间段内没有发送数据则终止连接.

此问题和其他此类问题(两个端点之间的任何位置)可能意味着如果您尝试在合理的空闲时段之后发送数据,则连接将不再"起作用".但是,在尝试发送数据之前,您可能不会发现这一点.

使用Keepalive既可以降低连接在线路中断的可能性,也可以让您更快地找到断开的连接.

  • 这是一个很好的观点,并且提醒我们要记住的不仅仅是我们自己直接实施的内容.另外,旅鼠!! (4认同)
  • 不一定,TCP连接由4个元素标识:src ip,src port,dest ip,dest port.因此,只要目标IP不同,您就可以重用相同的外部(源)端口. (4认同)