Java 通过套接字发送对象会导致SocketException

Java 通过套接字发送对象会导致SocketException,java,sockets,serversocket,Java,Sockets,Serversocket,如何阻止SocketException发生 我正在尝试将序列化对象从客户端简单地传输到本地计算机上的服务器 我已经能够使用以下代码的细微变化来发送字符串,但是当我尝试发送对象时 Customer customerToReceive = (Customer) input.readObject();// EXCEPTION OCCURS RIGHT HERE 我得到了一个SocketException,我不知道如何解释它 java.net.SocketException: Connection r

如何阻止SocketException发生

我正在尝试将序列化对象从客户端简单地传输到本地计算机上的服务器

我已经能够使用以下代码的细微变化来发送字符串,但是当我尝试发送对象时

Customer customerToReceive = (Customer) input.readObject();// EXCEPTION OCCURS RIGHT HERE
我得到了一个SocketException,我不知道如何解释它

java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at java.io.ObjectInputStream$PeekInputStream.read(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.read(Unknown Source)
at java.io.ObjectInputStream$BlockDataInputStream.readFully(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at MattServer.runCustomerServer(MattServer.java:44)
at MattServer.<init>(MattServer.java:14)
at MattServerTest.main(MattServerTest.java:10)
不要从客户端调用output.reset();似乎这可能导致服务器认为连接已重置。不相关,但您可能也不需要flush()


我也不认为应该在for循环的每个循环中创建新的ObjectOutputStream。在循环外部(上方)初始化并重新使用它。

此异常是过早关闭连接的结果,即客户端发送数据并立即关闭连接。在这种情况下,服务器通常无法读取客户端发送的所有数据,并且连接的终止出现在服务器端所看到的传输的中间。 请注意,
flush()
-ing网络
OutputStream
并不意味着或保证任何/所有数据都已实际传输。在最好的情况下,这只能确保所有数据都传递到本地计算机上的网络堆栈,而本地计算机将自行决定何时实际传输数据

一种解决方案是让服务器在准备好连接时关闭连接。然后,客户端应等待,例如在阻塞
read()
操作中,然后在服务器发出传输结束信号时,将收到指定异常的通知

另一种方法是实现您自己的确认,以便客户端将等待服务器发送回确认消息,然后双方可以安全地关闭连接

作为参考,在类似您的情况下,有一些套接字选项会影响连接的行为,即:

(这些套接字选项不是问题的解决方案,但在某些情况下可能会改变观察到的行为。)

编辑:

看来,
SO_LINGER
选项对于观察到的行为的相关性并不像我从参考文档中想象的那样明显。因此,我会尽量澄清这一点:

在Java中,基本上有两种通过
close()
终止TCP连接的方法:

  • 不启用
    SO\u LINGER
    选项。在这种情况下,会立即发送TCP连接重置(
    RST
    ),并返回对
    close()
    的调用。在收到
    RST
    后尝试使用连接(读或写)时,连接的对等端将收到一个异常,说明“连接已重置”

  • 启用了
    SO\u LINGER
    选项。在这种情况下,将生成一个TCP
    FIN
    ,以有序地关闭连接。然后,如果对等方没有确认在给定的时间范围内发送的数据,则会发生超时,并且发出
    close()
    的本地方会像案例1一样继续发送
    RST
    ,然后声明连接“已断开”

  • 因此,通常需要启用
    So_LINGER
    ,以允许对等方处理数据,然后干净地断开连接。如果未启用
    SO_LINGER
    ,并且在对等方处理所有数据之前调用了
    close()
    (即“太早”),则命名的异常会在对等方发生重置连接


    如上所述,
    TCP\u NODELAY
    选项可能以不确定的方式更改观察到的行为,因为在调用
    close()
    导致连接重置之前,写入的数据更有可能已经通过网络传输。

    此异常有几个原因,但最常见的情况是,您向另一端已关闭的连接进行了写入。换句话说,就是应用程序协议问题:您写入的数据多于读取的数据

    在本例中,这是因为您在套接字的生命周期中使用一个ObjectInputStream,但在每条消息中使用一个新的ObjectOutputStream,这是不可能工作的。在连接的寿命期内,两端各使用一个

    您还必须在接收器的IOException中收到“无效类型代码AC”消息。你知道吗

    编辑:对于本帖其他地方所预示的“提前结束”谬论的拥护者:

  • 状态(a)‘关闭是一种操作,意思是‘我没有更多的数据要发送’,(b)‘TCP将可靠地提供连接关闭前发送的所有缓冲区’,(c)‘本地用户启动关闭:在这种情况下,可以构造FIN段并将其置于传出段队列’[我的重点]。FIN不会在任何挂起数据之前到达,并且关闭不会中止挂起数据的传输


  • 设置一个正的
    SO_LINGER
    timeout会导致close()阻塞,直到发送所有挂起的数据或超时过期。如果根本不设置
    SO_LINGER
    ,挂起的数据仍将发送,但除非设置了+ve LINGER超时,否则将异步发送到关闭。第三种可能是故意丢弃挂起的数据并发送RST而不是FIN。OP在这里没有这样做,它也不是一个已经涉及RST的问题的解决方案

  • 几年前,我在互联网上做了一个吞吐量实验,在这个实验中,我改变了所有可能的TCP参数,除了
    SO\u LINGER
    ,我将其保留在默认状态。每个测试都包括一个数兆字节的定时单向传输,然后是一个正常的关闭,没有任何积极的“延迟”超时或等待对等方。在1700个连接中,零次遇到“连接重置”。责任在你身上
    public MattClient()
    {
        runCustomerClient();
    }
    
    public void runCustomerClient()
    {
        try
        {
            //Connection:
            System.out.println("Attempting connection...");
            client = new Socket("localhost",12345);
            System.out.println("Connected to server...");
    
            //Connect Streams:
    
            //output.flush();
            System.out.println("Got IO Streams...");
    
            //SEND MESSAGES:
            try
            {
                for(int i = 1;i<=10;i++)
                {
                    output = new ObjectOutputStream(client.getOutputStream());
                    Customer customerToSend = new Customer("Matt", "1234 fake street", i);
                    System.out.println("Created customer:");
                    System.out.println(customerToSend.toString());
                    output.writeObject(customerToSend);
                    output.flush();
                };
                message = "TERMINATE";
                System.out.println(message);
                output.writeObject(message);
                output.reset();
                output.flush();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            catch(Exception e2)
            {
                e2.printStackTrace();
            }
            finally
            {
    
            }
        }
        catch (IOException e1)
        {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        catch(Exception e3)
        {
            e3.printStackTrace();
        }
        finally
        {
    
        }
    }
    
    public class MattServer
    {
    ServerSocket server;
    Socket socket;
    ObjectInputStream input;
    ObjectOutputStream output;
    String message;
    
    public MattServer()
    {
        runCustomerServer();
    }
    
    public void runCustomerServer()
    {
        try
        {
            server = new ServerSocket(12345,100000);
            while(true)
            {
                //CONNECTION:
                System.out.println("Waiting for connection");
                socket = server.accept();
                System.out.println("Connection received...");
    
                //CONNECT STREAMS:
                //output = new ObjectOutputStream(socket.getOutputStream());
                //output.flush();
                input = new ObjectInputStream(socket.getInputStream());
                System.out.println("Got IO Streams...");
    
                //PROCESS STREAMS:
                System.out.println("Connection successful!");
                do
                {
                    System.out.println("Started loop");
                    try
                    {
                        System.out.println("in try...");
                        System.out.println(socket.getInetAddress().getHostName());
                        Customer customerToReceive = (Customer) input.readObject();// EXCEPTION OCCURS RIGHT HERE
                        Object o = input.readObject();
                        System.out.println("Object of class " + o.getClass().getName() + " is " + o);
                        System.out.println("Got customer object");
                        System.out.println(customerToReceive.toString());
                    }
                    catch(ClassNotFoundException cnfE)
                    {
                        System.out.println("Can't convert input to string");
                    }
                } while(!message.equals("TERMINATE"));
    
                System.out.println("Finished.");
    
            }
        }
        catch(IOException ioE)
        {
            ioE.printStackTrace();
        }
        finally
        {
            try
            {
                input.close();
                socket.close();
                server.close();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }