Java 网络程序中有时会丢失通过套接字发送的字符串中的最后几个字符
Last few chars in a string sent over socket sometimes missing in Java network program
现在,我正在尝试编写一个基于 GUI 的 Java 井字游戏,该游戏可通过网络连接运行。它在这一点上基本上可以工作,但是我有一个间歇性错误,在游戏过程中,通过网络连接发送的几个字符丢失了。一个案例是这样的,当 println 语句被添加到消息 sends/reads:
选手1:
刚刚发送第 14 列第 11 列 GAMEOVER true
玩家2:
刚收到 ROW 14 COLUMN 11 GAMEOV
我很确定当我通过网络阅读时发生了错误。读取发生在它自己的线程中,BufferedReader 包裹在套接字的 InputStream 周围,看起来像这样:
try {
int input;
while((input = dataIn.read()) != -1 ){
char msgChar = (char)input;
String message = msgChar + "";
while(dataIn.ready()){
msgChar = (char)dataIn.read();
message+= msgChar;
}
System.out.println("Just received " + message);
this.processMessage(message);
}
this.sock.close();
}
我的 sendMessage 方法非常简单,(只是写一个 DataOutputStream 包裹在套接字的输出流上)所以我认为问题不在那里发生:
try {
dataOut.writeBytes(message);
System.out.println("Just sent " + message);
}
如有任何想法,我们将不胜感激。谢谢!
尝试刷新发送方的 OutputStream。最后的字节可能保留在某些内部缓冲区中。
事实证明,ready() 方法仅保证下一次读取不会阻塞。因此,!ready() 不保证下一次读取会阻塞。就是这样。
我认为这里的问题与 TCP 堆栈本身有关。作为面向流的,当字节被写入套接字时,TCP 不保证它发送的字节的顺序或分组。我怀疑 TCP 堆栈以一种对它有意义的方式分解发送的字符串,并且在此过程中,ready() 方法必须检测流中的某种潜在中断,并且 return false ,尽管有更多信息可用。
我重构了代码,为每条消息发送添加了一个换行符,然后简单地执行了一个 readLine() 来代替。这允许我的网络协议依赖于换行符作为消息定界符,而不是 ready() 方法。我很高兴地说这解决了问题。
感谢您的参与!
使用什么类型的流对象来处理数据非常重要。在我看来,这种故障排除是由于您使用 DataOutputStream 发送信息,但使用其他东西接收信息而造成的。尝试分别通过 DataOutputStream 和 DataInputStream 发送和接收信息。
事实上,如果您通过调用 dataOut.writeBoolean(b) 发送内容
但是试图通过调用 dataIn.readString() 来接收这个东西,你最终什么也得不到。 DataInputStream 和 DataOutputStream 是类型敏感的。尝试重构您的代码,牢记这一点。
此外,一些输入流 return 在调用 read() 单个字节 时。这里你尝试将这个一个字节转换成char,而在java中char默认由两个字节.[=11组成=]
msgChar = (char)dataIn.read();
检查是否是数据丢失的原因
现在,我正在尝试编写一个基于 GUI 的 Java 井字游戏,该游戏可通过网络连接运行。它在这一点上基本上可以工作,但是我有一个间歇性错误,在游戏过程中,通过网络连接发送的几个字符丢失了。一个案例是这样的,当 println 语句被添加到消息 sends/reads:
选手1: 刚刚发送第 14 列第 11 列 GAMEOVER true
玩家2: 刚收到 ROW 14 COLUMN 11 GAMEOV
我很确定当我通过网络阅读时发生了错误。读取发生在它自己的线程中,BufferedReader 包裹在套接字的 InputStream 周围,看起来像这样:
try {
int input;
while((input = dataIn.read()) != -1 ){
char msgChar = (char)input;
String message = msgChar + "";
while(dataIn.ready()){
msgChar = (char)dataIn.read();
message+= msgChar;
}
System.out.println("Just received " + message);
this.processMessage(message);
}
this.sock.close();
}
我的 sendMessage 方法非常简单,(只是写一个 DataOutputStream 包裹在套接字的输出流上)所以我认为问题不在那里发生:
try {
dataOut.writeBytes(message);
System.out.println("Just sent " + message);
}
如有任何想法,我们将不胜感激。谢谢!
尝试刷新发送方的 OutputStream。最后的字节可能保留在某些内部缓冲区中。
事实证明,ready() 方法仅保证下一次读取不会阻塞。因此,!ready() 不保证下一次读取会阻塞。就是这样。
我认为这里的问题与 TCP 堆栈本身有关。作为面向流的,当字节被写入套接字时,TCP 不保证它发送的字节的顺序或分组。我怀疑 TCP 堆栈以一种对它有意义的方式分解发送的字符串,并且在此过程中,ready() 方法必须检测流中的某种潜在中断,并且 return false ,尽管有更多信息可用。
我重构了代码,为每条消息发送添加了一个换行符,然后简单地执行了一个 readLine() 来代替。这允许我的网络协议依赖于换行符作为消息定界符,而不是 ready() 方法。我很高兴地说这解决了问题。
感谢您的参与!
使用什么类型的流对象来处理数据非常重要。在我看来,这种故障排除是由于您使用 DataOutputStream 发送信息,但使用其他东西接收信息而造成的。尝试分别通过 DataOutputStream 和 DataInputStream 发送和接收信息。
事实上,如果您通过调用 dataOut.writeBoolean(b) 发送内容 但是试图通过调用 dataIn.readString() 来接收这个东西,你最终什么也得不到。 DataInputStream 和 DataOutputStream 是类型敏感的。尝试重构您的代码,牢记这一点。
此外,一些输入流 return 在调用 read() 单个字节 时。这里你尝试将这个一个字节转换成char,而在java中char默认由两个字节.[=11组成=]
msgChar = (char)dataIn.read();
检查是否是数据丢失的原因