9

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

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 reset by peer )”。之后程序关闭套接字,重新初始化它:

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

// reinitialize
initSocket();

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

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

我将非常感谢任何想法和提示。

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


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

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

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

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

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

4

2 回答 2

2

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

于 2012-10-25T22:23:46.610 回答
1

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

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

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

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

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

于 2012-11-19T18:19:33.787 回答