OutputStream.write()成功但数据未传递

Val*_*lik 9 java android network-programming java-io

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

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

    os = new DataOutputStream(socket.getOutputStream());
    is = new DataInputStream(socket.getInputStream());
}
Run Code Online (Sandbox Code Playgroud)

然后定期(每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);
}
Run Code Online (Sandbox Code Playgroud)

它有效.但是......有时(很少,每天可能是1或2次)我的服务器不接收数据.我的客户端日志如下:

Written A
Written B
Written C
Written D
Written E
Run Code Online (Sandbox Code Playgroud)

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

Received A
Received E
Run Code Online (Sandbox Code Playgroud)

没有收到B,C,D数据记录,尽管事实上在客户端看起来所有数据都是在没有任何例外的情况下发送的!

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

我真的不知道什么是错的.数据似乎是由客户端发送的,但它永远不会到达服务器端.我也用代理检查了它.

我只有日志而且我无法重现这个问题(但是我的客户每天都会发生一次这种情况)并且在日志中有时我看到连接因异常而被破坏"发送失败:ECONNRESET(连接由同行重置) )".之后,程序关闭套接字,重新初始化它:

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

// reinitialize
initSocket();
Run Code Online (Sandbox Code Playgroud)

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

可能与ECONNRESET有关可能不是,但我想提一下,因为它可能很重要.

我会非常感谢任何想法和提示.

PS也许它扮演一些角色:客户端代码运行在移动的Android移动设备上(它在汽车中).通过GPRS建立互联网连接.


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

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

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

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

BTW,因为连接再次返回available()返回0.

Val*_*lik 1

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

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

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

但如果服务器也关闭了套接字,则服务器可以读取该数据。所以解决办法应该是服务器端超时后关闭socket。

不幸的是,就我而言,这是不可行的,因为服务器是专有的第三方软件。

  • 所以服务器出现了严重的问题。它在原始连接上的读取应该超时或永远阻塞,或者获得 EOS,导致它关闭该连接;它应该成功地从新连接读取新数据。如果不是,则它是单线程的或存在其他并发问题。我不明白为什么你接受这个作为你自己问题的答案,因为它根本没有回答它,只是澄清了它发生的错误情况。不是答案。 (2认同)