- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我在理解 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/
我需要修复 getLineNumberFor 方法,以便如果 lastName 的第一个字符位于 A 和 M 之间,则返回 1;如果它位于 N 和 Z 之间,则返回 2。 在我看来听起来很简单,但我不
您好,感谢您的帮助!我有这个: 0 我必须在每次点击后增加“pinli
Javascript 中是否有一种方法可以在不使用 if 语句的情况下通过 switch case 结构将一个整数与另一个整数进行比较? 例如。 switch(integer) { case
我有一列是“日期”类型的。如何在自定义选项中使用“之间”选项? 最佳答案 请注意,您有2个盒子。 between(在SQL中)包含所有内容,因此将框1设置为:DATE >= startdate,将框2
我有一个表,其中包含年、月和一些数字列 Year Month Total 2011 10 100 2011 11 150 2011 12 100 20
这个问题已经有答案了: Extract a substring between double quotes with regular expression in Java (2 个回答) how to
我有一个带有类别的边栏。正如你在这里看到的:http://kees.een-site-bouwen.nl/ url 中类别的 ID。带有 uri 段(3)当您单击其中一个类别时,例如网页设计。显示了一
这个问题在这里已经有了答案: My regex is matching too much. How do I make it stop? [duplicate] (5 个答案) 关闭 4 年前。 我
我很不会写正则表达式。 我正在尝试获取括号“()”之间的值。像下面这样的东西...... $a = "POLYGON((1 1,2 2,3 3,1 1))"; preg_match_all("/\((
我必须添加一个叠加层 (ImageView),以便它稍微移动到包含布局的左边界的左侧。 执行此操作的最佳方法是什么? 尝试了一些简单的方法,比如将 ImageView 放在布局中并使用负边距 andr
Rx 中是否有一些扩展方法来完成下面的场景? 我有一个开始泵送的值(绿色圆圈)和其他停止泵送的值(簧片圆圈),蓝色圆圈应该是预期值,我不希望这个命令被取消并重新创建(即“TakeUntil”和“Ski
我有一个看起来像这样的数据框(Dataframe X): id number found 1 5225 NA 2 2222 NA 3 3121 NA 我有另一个看起来
所以,我正在尝试制作正则表达式,它将解析存储在对象中的所有全局函数声明,例如,像这样 const a = () => {} 我做了这样的事情: /(?:const|let|var)\s*([A-z0-
我正在尝试从 Intellivision 重新创建 Astro-Smash,我想让桶保持在两个 Angular 之间。我只是想不出在哪里以及如何让这个东西停留在两者之间。 我已经以各种方式交换了函数,
到处检查但找不到答案。 我有这个页面,我使用 INNER JOIN 将两个表连接在一起,获取它们的值并显示它们。我有这个表格,用来获取变量(例如开始日期、结束日期和卡号),这些变量将作为从表中调用值的
我陷入了两个不同的问题/错误之间,无法想出一个合适的解决方案。任何帮助将不胜感激 上下文、FFI 和调用大量 C 函数,并将 C 类型包装在 rust 结构中。 第一个问题是ICE: this pat
我在 MySQL 中有一个用户列表,在订阅时,时间戳是使用 CURRENT_TIMESTAMP 在数据库中设置的。 现在我想从此表中选择订阅日期介于第 X 天和第 Y 天之间的表我尝试了几个查询,但不
我的输入是开始日期和结束日期。我想检查它是在 12 月 1 日到 3 月 31 日之间。(年份可以更改,并且只有在此期间内或之外的日期)。 到目前为止,我还没有找到任何关于 Joda-time 的解决
我正在努力了解线程与 CPU 使用率的关系。有很多关于线程与多处理的讨论(一个很好的概述是 this answer )所以我决定通过在运行 Windows 10、Python 3.4 的 8 CPU
我正在尝试编写 PHP 代码来循环遍历数组以创建 HTML 表格。我一直在尝试做类似的事情: fetchAll(PDO::FETCH_ASSOC); ?>
我是一名优秀的程序员,十分优秀!