【问题标题】:Why am I sending an RST if my socket is connected and not closed?如果我的套接字已连接且未关闭,为什么要发送 RST?
【发布时间】:2017-11-24 21:43:35
【问题描述】:

我有一个使用 java.net.Socket 与 PC 进行无线通信的 Android 设备。一切都很好,但是如果我在 1 分钟内什么都不做(即没有使用网络),那么当 Android 向 PC 发送一个数据包时,PC 会接收它并发送一个 ACK​​,但 Android 会以 RST 响应。

来自 Wireshark(10.1.2.1 是 Android,10.1.2.11 是 PC)...

356 0.112470 10.1.2.1 10.1.2.11 TCP 97 34360→181 [PSH, ACK] Seq=1 Ack=1 Win=4935 Len=31 TSval=156103571 TSecr=320673352

359 0.000011 10.1.2.11 10.1.2.1 TCP 66 181→34360 [ACK] Seq=1 Ack=32 Win=260 Len=0 TSval=320738236 TSecr=156103571

360 0.000304 10.1.2.1 10.1.2.11 TCP 60 34360→181 [RST] Seq=32 Win=0 len=0

此时,如果我询问套接字的成员变量,它会说 . . .

  • isConnected = true
  • isCreated = true
  • isInputShutdown = 假
  • isOutputShutdown = 假
  • isClosed = 假
  • isBound = true

...看起来我应该仍然可以正常接收数据包。 那么我该如何弄清楚我为什么要发送 RST

注意 - 没有设置“睡眠”或关闭 wifi 或显示器或此设备上启用的任何其他省电功能设置。

【问题讨论】:

  • 当我运行wireshark时,第二列是wireshark捕获数据包时的时间戳,以秒为单位,单调递增。
  • 我的它设置为自上次捕获数据包以来的增量

标签: android sockets tcp


【解决方案1】:

1 分钟的延迟看起来像是超时。它可能是SO_TIMEOUT,但这不会自行生成网络活动。此外,发送的最后一个数据包包含 31 个字节的数据这一事实似乎表明涉及应用程序。一种可能的情况是:

  • android 应用程序超时(自行超时或由套接字的SO_TIMEOUT 触发)
  • 它发送最后一块数据,例如通过刷新输出流。
  • 它会突然关闭套接字,例如使用setSoLinger(true, 0) 套接字选项。

【讨论】:

    【解决方案2】:

    那些Socket 方法都没有返回连接的状态。它们都是关于java.net.Socket object 的内部状态,由您调用它的构造函数和方法决定。如果对等方断开连接,它们不会神奇地开始返回 false

    您会发现,当您使用用于 I/O 的套接字时,您会得到一个IOException: 'connection reset'

    为什么要重置连接是另一回事。通常的原因是你发送到一个已经被对等体关闭的连接,或者对等体关闭了连接而没有读取所有已经到达的数据。换句话说,应用程序协议错误。还有其他原因,但这些是最常见的。

    【讨论】:

    • 所以我必须在 Android 中保持 polling 吗?但我认为 Android 编程范式是事件驱动的。我的一位同事在 PC 上维护了一个程序,该程序使用 Winsock 在 VB 中执行类似于我的应用程序的操作,他可以​​简单地注册一个事件,如果连接中断,它就会被调用。我已经测试了他的程序并且它有效。我发布了一个关于它的问题:[stackoverflow.com/questions/44681648/…(链接)
    • 我没有说任何关于投票的事情。我说只使用套接字。 java.net.Socket 不是基于事件的。
    • 轮询是对设备或进程进行同步采样以确定其状态。所以“使用”套接字只是为了测试似乎对我来说是轮询。无论如何,根据 Wireshark Android 正在发送 RST,这是连接断开时发生的第一件事。那么我该如何弄清楚 Android 发送 RST 的原因呢?
    • 我也没有说过使用套接字“只是为了测试”。我不提倡以任何形式或形式。我提倡的就是我所说的:使用套接字'for I/O',即正常情况下,并在出现异常时处理它们,而不是试图通过像isConnected() 这样的调用来预测进一步的情况。我已经回答了你的最后一个问题。
    【解决方案3】:

    这可能是由于您的 NAT 路由器在超时后尝试关闭套接字以释放资源造成的。打开 keep_alive 可能会有所帮助。您也可以尝试使用绕过路由器的静态 IP 地址创建自己的网络来排除这种假设。

    【讨论】:

      【解决方案4】:

      当连接关闭时,isClosed() 方法应该返回 TRUE。 即使连接已关闭,isConnected() 方法也可以返回 TRUE,因为该方法告诉您的是“如果您成功连接”,则在您断开连接时变量状态不会更改/刷新。

      但是,在您的情况下,连接肯定没有按照文档关闭。 欲了解更多信息,请参阅:

      https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isConnected--

      https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#isClosed--

      注意:设置为 TRUE setKeepAlive(boolean)

      可能会有所帮助

      另见:

      https://docs.oracle.com/javase/8/docs/api/java/net/SocketOptions.html#SO_KEEPALIVE

      https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html#setKeepAlive-boolean-

      回到问题所在,检查以下情况之一:

      1. 您能否确保由于某种原因没有其他设备 和安卓一样的IP?也关掉你的 3G/4G 并留下来 仅在无线网络中连接。
      2. 有路由器做 NAT 吗?在这种情况下,路由器可以发送 重置标志。

      如果一切正常(您的 Android 手机回复 RST)并且 SO_KEEPALIVE 不起作用,则可能是一些应用程序级别的错误导致连接中断。

      【讨论】:

      • 这不是一部可以访问电信网络的手机,所以不用担心 3G/4G。没有路由器做 NAT - 这只是一台 PC、一个 WAP 和 Android 设备。 可能是一些应用程序级别的错误导致连接中断 -- 我不确定你的意思。 “终止连接”是什么意思,我会在套接字状态或显示“终止”连接的跟踪中寻找什么? (或者如果连接被“终止”,我可以注册哪些事件?)
      • @user316117 你设置了 setKeepAlive(true); ?还是同样的问题?
      • keepalive 对于电池供电的设备不是一个好主意,因为它会耗尽电池。这对诊断 Android 发送 RST 的原因有何帮助?
      • 本地套接字 关闭时,isClosed() 应该返回 true,并且在其他任何时候,无论连接状态如何。你在这里与你自己的 other answer 在这个话题上相矛盾。
      • @EJP 我在哪里与我的其他答案相矛盾?我说的完全一样。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 2013-07-23
      • 1970-01-01
      • 2018-06-24
      相关资源
      最近更新 更多