Java 通过套接字发送对象会导致SocketException
如何阻止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
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
选项。在这种情况下,将生成一个TCPFIN
,以有序地关闭连接。然后,如果对等方没有确认在给定的时间范围内发送的数据,则会发生超时,并且发出close()
的本地方会像案例1一样继续发送RST
,然后声明连接“已断开”So_LINGER
,以允许对等方处理数据,然后干净地断开连接。如果未启用SO_LINGER
,并且在对等方处理所有数据之前调用了close()
(即“太早”),则命名的异常会在对等方发生重置连接
如上所述,
TCP\u NODELAY
选项可能以不确定的方式更改观察到的行为,因为在调用close()
导致连接重置之前,写入的数据更有可能已经通过网络传输。此异常有几个原因,但最常见的情况是,您向另一端已关闭的连接进行了写入。换句话说,就是应用程序协议问题:您写入的数据多于读取的数据
在本例中,这是因为您在套接字的生命周期中使用一个ObjectInputStream,但在每条消息中使用一个新的ObjectOutputStream,这是不可能工作的。在连接的寿命期内,两端各使用一个
您还必须在接收器的IOException中收到“无效类型代码AC”消息。你知道吗
编辑:对于本帖其他地方所预示的“提前结束”谬论的拥护者:
SO_LINGER
timeout会导致close()阻塞,直到发送所有挂起的数据或超时过期。如果根本不设置SO_LINGER
,挂起的数据仍将发送,但除非设置了+ve LINGER超时,否则将异步发送到关闭。第三种可能是故意丢弃挂起的数据并发送RST而不是FIN。OP在这里没有这样做,它也不是一个已经涉及RST的问题的解决方案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();
}
}
}