【问题标题】:OutputStream.write() successful but the data is not delivered [closed]OutputStream.write() 成功但数据未传递[关闭]
【发布时间】:2012-10-16 02:00:34
【问题描述】:

在写入套接字时我有一个非常奇怪的行为。 在我的移动客户端中,我使用了一个初始化如下的套接字:

private void initSocket()
{
    socket = new Socket();
    socket.connect(new InetSocketAddress(host, port));

    os = new DataOutputStream(socket.getOutputStream());
    is = new DataInputStream(socket.getInputStream());
}

然后定期(每 60 秒)我向这个套接字读取和写入一些数据(这里的代码稍微简化了一点):

if(!isSocketInitialized())
{
    initSocket();
}

byte[] msg = getMessage();

os.write(msg);
os.flush();

int bytesAvailable = is.available( );
if(bytesAvailable>0)
{
    byte[] inputBuffer = new byte[bytesAvailable];

    int numRead = is.read(inputBuffer, 0, bytesAvailable);
    processServerReply(inputBuffer, numRead);
}

而且它有效。但是...有时(非常罕见,可能每天 1 或 2 次)我的服务器不接收数据。我的客户日志如下所示:

Written A
Written B
Written C
Written D
Written E

等等。但在服务器端它看起来像:

Received A
Received E

未收到 B、C、D 数据记录,尽管事实上在客户端看起来所有数据都已发送,没有任何异常!

这种差距可能很小(2-3 分钟),这不是很糟糕,但有时它们可​​能非常大(1-2 小时 = 60-120 个周期),这对我的客户来说确实是个问题。

我真的不知道有什么问题。数据似乎是由客户端发送的,但它从未到达服务器端。我也用代理检查过。

我只有日志,无法重现此问题(但我的客户每天都会发生不止一次),有时我在日志中看到连接中断并出现异常“sendto failed: ECONNRESET (Connection由同行重置)”。之后程序关闭套接字,重新初始化它:

// close
is.close();
os.close();
socket.close();

// reinitialize
initSocket();

并尝试如上所述再次写入数据。然后我看到了问题:连接建立,写入成功,但是没有数据到达服务器!

可能与 ECONNRESET 有关系,可能没有,但我想提一下,因为它可能很重要。

如果有任何想法和提示,我将不胜感激。

附:也许它起到了一些作用:客户端代码在移动的 Android 移动设备上运行(它在汽车中)。互联网连接是通过 GPRS 建立的。


UPD:我可以重现它!至少部分(客户端发送 A、B、C、D、E 而服务器只接收 A)。每次都会发生,如果:

  1. 连接建立,客户端读写->OK

  2. 连接丢失(我关闭了我的 WLAN 路由器 :)),我变成了 IOException,我关闭了流和套接字 -> 好的

  3. 我打开我的路由器,连接回来了,我再次初始化套接字,程序执行 write() 无异常,但是......没有数据到达服务器。

顺便说一句,由于连接再次返回,available() 总是返回 0。

【问题讨论】:

  • 拥有一个可以尝试与设备同步的服务器可能会有所帮助。如果它第一次错过了数据,它总是可以重试 - 取决于您轮询数据的频率。
  • @max,当然,你是对的。但是服务器是封闭软件,我无法更改协议。
  • 你不能用ByteArrayOutputStream吗?某事like this
  • @Asok 是的,我可以。但为什么?为什么要解决这个问题?
  • @Asok 没问题,你不应该道歉 :) 我感谢每一个建议!

标签: java android network-programming java-io


【解决方案1】:

摆脱available() 测试。如果对方要回复,请阅读它。否则,有时您会阅读回复,有时则不会,并且您会失去同步。 available() 的正确用法很少,这不是其中之一。

【讨论】:

  • 谢谢你的建议,我正在重写代码,这样我就可以永久读取数据而不会阻塞写入(顺便说一句,你能推荐一些关于线程和网络的阅读吗?)但是......你能不能解释您的建议如何帮助解决问题?我认为我在关闭或初始化时做错了。所以套接字是“打开的”和“连接的”,我可以“写”等,但没有数据传送到服务器。
  • @Valelik 根据到达的速度选择性地读取响应不可能是正确的,不是吗?您现在所做的将导致各种可以想象的问题,从您刚刚没有读取的“丢失数据”到写入块,再到“对等方重置连接”。让我们知道您尝试时发生了什么。
  • 我再次重写了我的通讯。现在我有一个额外的阅读线程。所以我现在不使用available()。但这没有帮助。我也有同样的问题。
  • 我不同意这不是对 available() 的正确使用(虽然 Valelik 使用它来确定 byte[] 数组大小的部分肯定是错误的)。如果您需要从多个(可能是无限的)流中读取并且不想创建潜在的无限线程来为它们提供服务,那么 available() > 0 在我看来是一个不错的方法 - 它类似于 select C 中的 () 方法。但是,您必须记住在迭代之间 sleep() 以避免始终以 100% cpu 负载运行您的软件。
  • @gardarh 您刚才描述的是available()不同用法。 这个包括只读取立即到达的回复,这将在一段时间内失去同步。
【解决方案2】:

这种奇怪行为的原因是服务器端未关闭的套接字。我可以用小型客户端和服务器重现它。两者都由几行代码组成,它们执行以下操作:

  1. 连接到服务器
  2. 模拟死角(例如关闭 WiFi)
  3. 关闭客户端的套接字
  4. 不要关闭服务器端的套接字
  5. 打开你的 WiFi
  6. 从客户端建立新连接
  7. 向此连接写入数据

瞧!客户端写入数据没有任何错误,但服务器没有收到它......

但是如果服务器也关闭了套接字,那么服务器就可以读取这些数据。所以解决方案应该是在服务器端超时后关闭套接字。

不幸的是,在我的情况下它不可行,因为服务器是专有的第三方软件。

【讨论】:

  • 所以服务器出现了严重问题。它对原始连接的读取应该超时或永远阻塞,或者得到一个导致它关闭该连接的 EOS;它应该成功地从新连接中读取新数据。如果不是,它是单线程的或有其他一些并发问题。我不明白为什么你接受它作为你自己问题的答案,而它根本没有回答,只是澄清了它发生的错误情况。没有答案。
猜你喜欢
  • 1970-01-01
  • 2015-03-31
  • 1970-01-01
  • 2012-11-13
  • 2011-10-13
  • 2021-04-07
  • 2019-02-08
  • 1970-01-01
  • 2021-04-06
相关资源
最近更新 更多