【问题标题】:Prevent TCP socket connection retries防止 TCP 套接字连接重试
【发布时间】:2014-03-14 22:55:31
【问题描述】:

如何防止 TCP 进行多次套接字连接尝试?

背景

我正在尝试粗略估计到客户的往返时间。我必须使用的高级协议无法确定 RTT,也没有任何类型的无操作请求/响应流。所以,我试图直接从较低层获取信息。特别是,我知道客户端会主动拒绝特定端口上的 TCP 连接尝试。

Me -> Client: SYN
Client -> Me: ACK, RST

代码

  long lStartTime = System.nanoTime() / 1000000;
  long lEndTime;

  // Attempt to connect to the remote party.  We don't mind whether this
  // succeeds or fails.
  try
  {
    // Connect to the remote system.
    lSocket.connect(mTarget, MAX_PING_TIME_MS);

    // Record the end time.
    lEndTime = System.nanoTime() / 1000000;

    // Close the socket.
    lSocket.close();
  }
  catch (SocketTimeoutException|IOException lEx)
  {
    lEndTime = System.nanoTime() / 1000000;
  }

  // Calculate the interval.
  lInterval = lEndTime - lStartTime;
  System.out.println("Interval = " + lInterval);

问题

使用 Wireshark,我看到对 lSocket.connect 的调用在放弃之前进行了三次(失败)尝试连接套接字 - 显然是任意的尝试间间隔(通常约为 300 毫秒)。

Me -> Client: SYN
Client -> Me: ACK, RST
Me -> Client: SYN
Client -> Me: ACK, RST
Me -> Client: SYN
Client -> Me: ACK, RST

问题

有没有办法让 TCP 在单个 SYN/RST 对之后放弃?

我浏览了一些 Java 代码。当AbstractPlainSocketImpl 上的评论说时,我想知道我是不是赢了……

/**
 * The workhorse of the connection operation.  Tries several times to
 * establish a connection to the given <host, port>.  If unsuccessful,
 * throws an IOException indicating what went wrong.
 */

...但遗憾的是,没有证据表明该函数或我查看过的任何其他(非本地)函数中存在循环/重试。

这种重试行为实际上来自哪里?又如何控制?

替代品

我也可能对替代品持开放态度,但不是……

  • 使用 ICMP 回显请求(ping)。我知道很多客户不会回复他们。
  • 使用原始套接字。其中一个平台是 Windows,如今它严重限制了使用原始套接字的能力。 (我还认为,如果 Linux 网络堆栈陷入尝试使用原始套接字执行 TCP 的应用程序的交火,它会毫无帮助地跳入。)
  • 使用 JNI,除非作为最后手段。我的代码需要在至少 2 个非常不同的操作系统上运行。

【问题讨论】:

  • 呃,我不明白...如果您收到 RST,您通常会收到 ECONNREFUSED,Java 会将其包装在 SocketException 中(不记得具体是哪个)跨度>
  • 我得到了异常(我最终进入了 IOException 分支),但只有在某个底层尝试并失败了 3 次之后。

标签: java sockets tcp language-agnostic


【解决方案1】:

TCP 连接重试是操作系统套接字实现的一个功能。配置这取决于平台。请参阅 https://security.stackexchange.com/questions/34607/why-is-the-server-returning-3-syn-ack-packets-during-a-syn-scan 了解这是什么以及为什么会发生这种情况。

在 Windows 上,您应该可以在注册表中修改重试次数:

该文档中也详细介绍了与 RTT 相关的设置。

在 Linux 上,链接的安全帖子中接受的答案讨论了如何配置此参数:

在 Linux 系统上,查看 /proc/sys/net/ipv4/ 中称为 tcp_syn_retriestcp_synack_retries 的特殊文件:它们包含内核在给定连接上发出 SYN(分别为 SYN+ACK)的次数......这个就像echo 3 &gt; tcp_synack_retries 一样简单...

请注意,这是系统范围的设置。

您可以通过读取注册表设置(在 Windows 上)或读取特殊文件的内容(在 Linux 上)读取当前值。

另外,MSDN 对 Windows 上的 TCP 连接 RTT 有这样的说法:

TCP/IP 会随着时间调整重新传输的频率。每个接口的原始传输和第一次重传之间的延迟由TcpInitialRTT 条目的值决定。默认为三秒。每次尝试后此延迟加倍。在最后一次尝试之后,TCP/IP 等待一个等于上次延迟两倍的时间间隔,然后放弃连接请求。

顺便说一句,re: raw sockets - 是的,你会遇到一个非常困难的时期。此外,从 Windows XP SP2 开始,Windows won't actually let you specify TCP protocol numbers for raw sockets under any circumstances(请参阅限制)。

另外,顺便说一句:确保 TCP 连接没有被客户端前面的单独防火墙阻止,否则您最终只能测量到防火墙的往返时间。

【讨论】:

  • 这回答了主要问题,谢谢。 (有趣的是,当远端发送 RST 而不是超时时,TcpInitialRTT 和加倍方法似乎都不适用。)有人对估计 RTT 有任何替代建议吗?
  • @AndrewRose 是的,我还观察到 TcpInitialRTT 的记录行为与您报告的行为不匹配;很抱歉耸耸肩而不是提及它。那个我无法解释。也许您可以改为连接到您知道打开的 TCP 端口(如果有)并测量执行此操作所需的时间;握手必须来回进行。但是,一般来说,由于相对于 ICMP 的来回复杂性,TCP 的任何使用都会给您的测量增加一些不准确性。
猜你喜欢
  • 2011-08-31
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
  • 2010-12-01
  • 2021-09-10
相关资源
最近更新 更多