【发布时间】:2014-04-08 08:40:06
【问题描述】:
我很难理解 Java 如何在 Windows 和 Linux 上处理套接字的差异 - 特别是当一方(客户端或服务器)突然关闭连接时。
我编写了以下非常简单的服务器和客户端类,以使我的观点尽可能简单、客观和易于理解:
SimpleClient.java:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class SimpleClient {
public static void main(String args[]) {
try {
Socket client_socket = new Socket("127.0.0.1", 9009);
// Used to read from a terminal input:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// Used for client/server communication:
BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream()));
while(true) {
System.out.print("Command: ");
String msg = br.readLine();
// Send:
out.write(msg);
out.newLine();
out.flush();
// Receive:
int ifirst_char;
char first_char;
if((ifirst_char = in.read()) == -1) { // Server Closed
System.out.println("Server was closed on the other side.");
break;
}
first_char = (char) ifirst_char;
msg = String.valueOf(first_char);
msg += in.readLine();
// Shows the message received from the server on the screen:
System.out.println(msg);
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
SimpleServer.java:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleServer {
public static void main(String args[]) {
try {
ServerSocket server_socket = new ServerSocket(9009);
Socket client_socket = server_socket.accept();
while(true) {
BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream()));
// Receive:
int ifirst_char;
char first_char;
if((ifirst_char = in.read()) == -1) { // Client Closed
System.out.println("Client was closed on the other side.");
break;
}
first_char = (char) ifirst_char;
String msg = msg = String.valueOf(first_char);
msg += in.readLine();
msg = "Server Received: " + msg;
// Send:
out.write(msg);
out.newLine();
out.flush();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
当然,我可以实现一个代码来正确关闭客户端或服务器,但正如我所说,目标是模拟任一端的突然关闭,其中无法发送或接收“断开代码”。这就是我创建这 2 个非常简单的类的原因。
在 Linux 上,它运行得很好:
$ java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
Server was closed on the other side.
$
但是在 Windows 上:
C:\simplesocket>java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
at sun.nio.cs.StreamEncoder.implFlush(Unknown Source)
at sun.nio.cs.StreamEncoder.flush(Unknown Source)
at java.io.OutputStreamWriter.flush(Unknown Source)
at java.io.BufferedWriter.flush(Unknown Source)
at SimpleClient.main(SimpleClient.java:32)
假设我尝试通过修改 SimpleClient.java 上的以下行来忽略此异常:
// Send:
try {
out.write(msg);
out.newLine();
out.flush();
}
catch(Exception e) {}
抛出另一个异常:
C:\simplesocket>java SimpleClient
Command: echo
Server Received: echo
Command: test
Server Received: test
Command: (server now was closed on the other side)
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at sun.nio.cs.StreamDecoder.readBytes(Unknown Source)
at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
at sun.nio.cs.StreamDecoder.read(Unknown Source)
at java.io.InputStreamReader.read(Unknown Source)
at java.io.BufferedReader.fill(Unknown Source)
at java.io.BufferedReader.read(Unknown Source)
at SimpleClient.main(SimpleClient.java:42)
我不知道代码上的相应行是否会是这些异常中指出的行,但第一行在 out.flush() 上抛出,第二行在 in 上抛出.read().
所以基本上,正如您在 Linux 上看到的那样,即使在突然关闭服务器之后:
1. 发送数据时不抛出异常。
2.更重要的是,当我尝试接收它时,第一个字符是“-1”并且接收正确。
在 Windows 上,它在发送时抛出异常,更重要的是在接收时抛出异常 - 当调用 read() 方法时 - 我无法获得“流结束”(-1) 代码。
这就引出了一些问题:
1. 为什么在 Windows x Linux 上会有如此大的差异?为什么在 Linux 上不会抛出这些异常,而在 Windows 上却会抛出这些异常?
2. 具有跨平台特性的 Java 不应该尽量减少在运行时的差异吗?两个系统? (顺便说一句,我在两者上都使用了 JDK 7)
3. 有没有办法更改代码以实现突然关闭并使其更“类似于 Linux” " 在 Windows 上,不抛出所有这些异常并在我的 in.read() 上获得 -1?
4. 如果没有,是否推荐任何外部 API?
我试图在网上搜索几个小时关于这个特定主题但没有成功。
我也尝试过很多解决方案,比如在client_socket上调用isConnected()、isBound()、isClosed()等方法客户端没有成功。他们总是说有一个活动的连接并且没有问题,即使在关闭服务器之后也是如此。
希望有人会花时间回答至少一个这些问题。
提前向您表示最诚挚的感谢。
【问题讨论】:
-
isXXX() 方法告诉你的是套接字的状态,而不是连接的状态。 Linux 套接字和 Windows 套接字之间的主要区别在于缓冲区的大小,仅此一项就足以解释您在此处看到的所有内容。这不是 Java 试图解决的问题。