我在理解
Java如何处理Windows和
Linux上的套接字方面遇到了很多麻烦 – 特别是当其中一方(客户端或服务器)突然关闭连接时.
我编写了以下非常简单的Server和Client类,以使我的观点变得简单,客观,并且易于理解:
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();
}
}
}
当然,我可以实现一个代码来正确关闭客户端或服务器,但正如我所说,目标是模拟任何一方的突然关闭,不能发送或接收“断开代码”.这就是我创建这两个非常简单的类的原因.
在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()而第二个抛出on.read().
所以基本上,正如你在Linux上看到的那样,即使在突然关闭服务器之后:
1.当我尝试发送数据时,它不会抛出异常.
2.更重要的是,当我尝试接收它时,第一个字符为“-1”并正确接收.
在Windows上,它在发送时抛出异常,更重要的是在接收时抛出异常 – 当调用read()方法时 – 我无法获得“流末尾”(-1)代码.
这引出了一些问题:
1.为什么Windows x Linux上有这么大的差异?为什么在Linux上这些例外不会在Windows上被抛出?
2.具有所有跨平台特性的Java是否应该尽量减少在两个系统中运行的差异? (顺便说一句,我在两者上使用JDK 7)
3.有没有办法改变突然关闭的代码并让它在Windows上更像“Linux-like”,而不抛出所有这些异常并在我的in.read()上得到-1?
4.如果没有,建议使用任何外部API?
我试图在网上搜索这个特定主题几个小时,但没有成功.
我也尝试了许多解决方案,比如在客户端的client_socket中调用isConnected(),isBound(),isClosed()等方法,但没有成功.即使在关闭服务器之后,他们总是说有一个活动连接并且没有问题.希望有人会花时间回答这些问题中的至少一个问题.
对于任何答案,您都会提前致以最诚挚的谢意.