gpt4 book ai didi

java - Windows 和 Linux 之间 Java 套接字的差异 - 如何处理它们?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:57:52 25 4
gpt4 key购买 nike

我在理解 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();
}
}
}


当然,我可以实现一个正确关闭客户端或服务器的代码,但正如我所说,目标是模拟任何一方的突然关闭,其中无法发送或接收“断开代码”。这就是我创建这两个非常简单的类的原因。
在 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. 有没有一种方法可以更改突然关闭的代码并使其在 Windows 上更“类似 Linux”地工作,而不会抛出所有这些异常并在我的 in.read 中获取 -1 ()??

4. 如果没有,是否推荐任何外部 API?


我试图在网上搜索这个特定主题的几个小时,但没有成功。

我也尝试了很多解决方案,比如在 client_socket客户端没有成功。他们总是说有一个活跃的连接并且没有问题,即使在关闭服务器之后也是如此。

希望有人能花时间至少回答其中一个问题。

提前对您的任何回答表示最诚挚的感谢。

最佳答案

您的代码没有任何关闭,所以我假设您实际上是指一个端点进程停止了,也就是被杀死了。

Unix 套接字 sd“只是”fd,当 Unix 进程结束时没有关闭 fd,包括 JVM 停止的情况并且您没有调用 close(或 shutdown-WR),fd 被操作系统关闭,对于 TCP 套接字(至少尝试)正常的又名优雅关闭:FIN/ACK 与 FIN-WAIT 和 TIME-WAIT 的交换。我知道制作 Unix 套接字的唯一方法在 TCP 级别 (RST) 执行无障碍关闭是在关闭之前将 linger 设置为 0(显式或通过退出)。中间件也有可能强行断开您与 RST 的连接,而且这种情况并不少见;例如我见过防火墙在 15 分钟不活动后重新启动您。我也很少看到伪造 FIN 的中间盒,或者尝试这样做但做错了。

Windows 套接字 (WinSock) 是一种不同于文件的 API。如果一个 Windows 进程在没有调用 closesocket 的情况下结束(类似于但与关闭分开)或至少 shutdown-WR,Winsock 执行 RST。要在 Windows 上正常关闭 (FIN),您(通过 JVM)必须调用其中之一。 JVM 大概可以跟踪 java.net.Sockets(但不是 JNI 中的任何)并在 JVM 退出时为您执行此操作,但是它没有;你可以要求改进。如果您使用 TaskMgr 或类似工具从外部杀死它,即使那样也可能不起作用,并且如果遇到 JVM 故障,可能无法正常工作:JVM 会 try catch 故障并提供小型转储,这将是一个可以尝试的地方清理套接字,但如果存在 JVM 错误,它可能会再次失败——而 IME 大多数 JVM 错误都是由 JVM 错误引起的。

如果足以处理代码错误(泄漏)和信号而不是 JVM 错误和故障,您可以将 Socket 子类化因此,如果强制(正常)关闭 .finalize 并使用 Runtime.addShutdownHook 退出,则改用它。

在 Unix 或 Windows 套接字中,收到的 FIN 被视为文件结尾,就像任何其他文件(例如磁盘文件)一样。收到的 RST 作为错误 [WSA]ECONNRESET 返回给 JVM,这会引发异常。隐藏这个不好差异,因为对于您以外的应用程序,它可能很重要——足以让某些协议(protocol)不得不更改防止假 FIN 成为安全漏洞,尤其是 SSLv2 和 HTTP/0.9。

如果您还考虑对等系统(不仅仅是 JVM)或网络的某些部分出现故障的情况,或者您的网络接口(interface)出现故障,您可以获得的异常会更加多样化。恕我直言,不要试图处理这些,只需报告您看到的内容,然后让系统管理员和网络管理员解决。我见过程序员遇到 Exception X 的情况由于实验室条件下的问题 P 并为此编码,但在现实世界中异常 X 发生在非常不同的情况下原因和“有帮助”的处理实际上使解决问题变得更加困难。

旁白:服务器应该在 while(true)do-a-line 循环之前创建 BufferedReader;如果你得到/想要一个一次发送多行的客户端,显示的代码将丢失数据。if first_char==-1 else convert to String; 你不需要那根头发;只需使用 in.readLine,它恰好返回 null与初始 in.read 返回 -1 的情况相同,对于 (TCP) Socket 是在收到 FIN 时。相反,应检查来自 System.in 的客户端 readLine;如果有人输入 ^Z 或 ^D 或任何你会得到 NPE 的东西。

关于java - Windows 和 Linux 之间 Java 套接字的差异 - 如何处理它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22931811/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com