Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows和Linux之间Java套接字的差异-如何处理它们?_Java_Sockets - Fatal编程技术网

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

Windows和Linux之间Java套接字的差异-如何处理它们?,java,sockets,Java,Sockets,我很难理解Java在Windows和Linux上处理套接字的不同之处,特别是当其中一方(客户端或服务器)突然关闭连接时 我编写了以下非常简单的服务器和客户机类,以使我的观点尽可能简单、客观且易于理解: SimpleClient.java: import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter;

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

    }
}
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();
        }
    }
}

SimpleServer.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();
        }

    }
}
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?


我试着在网上搜索了几个小时,但没有成功。

我还尝试了许多解决方案,比如在客户端的客户端套接字中调用方法,如isConnected()isBound()isClosed(),但都没有成功。他们总是说有一个活动连接,即使在关闭服务器后也没有问题。

希望有人能抽出时间回答至少一个问题。

对于您的回答,我将提前向您表示最诚挚的感谢。

您的代码没有完成任何关闭操作,因此我假设您实际上是指停止了一个端点进程

Unix套接字sd是“仅”fd,当Unix进程结束而不关闭fd时,包括JVM停止的情况 您还没有调用close(或shutdownwr),操作系统会关闭fd,而TCP套接字会关闭fd(至少会尝试) 正常关闭:FIN/ACK交换,FIN等待和TIME-WAIT。我知道的唯一一种制作Unix套接字的方法 TCP级别的do graceless close(RST)是在关闭之前(显式或通过退出)将linger设置为0。 中间盒强行中断您与RST的连接也是可能的,也并非罕见;例如,我见过防火墙 在15分钟不活动后,你会感觉到。我也很少看到中间商伪造FIN,或者试图做错事

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

若它足以处理代码错误(泄漏)和信号,但不足以处理JVM错误和故障,那个么您可以将Socket子类化 因此,如果强制(优雅地)关闭,请使用Runtime.addShutdownHook完成并退出,并改用它

在Unix或Windows套接字中,接收到的FIN被视为文件的结尾,就像任何其他文件(例如磁盘文件)一样。 接收到的RST作为错误[WSA]返回给JVM,这会引发异常。把这个藏起来是不好的 不同之处在于,对于你的应用程序以外的应用程序来说,它可能意义重大——足以让某些协议不得不改变 防止假冒FIN成为安全漏洞,特别是SSLv2和HTTP/0.9

如果您还考虑对等系统失败的情况(不仅仅是JVM),或者网络的某些部分失败, 或者您的网络接口出现故障,您可能会遇到更多的异常。我不想处理这些, 只需报告您所看到的内容,并让系统管理员和网络管理员进行整理。我见过程序员出现异常X的情况 由于实验室条件下的问题P,并为此进行了编码,但在现实世界中,例外情况X发生在非常不同的情况下 原因和“有益的”处理实际上使问题更难解决

旁白:服务器应该在不进入while(true)do-a-line循环之前创建BufferedReader