问题:
一个java socket网络编程的引发的异常,具体代码Client.java、Server.java,如下
Client.java
package Test2;import java.io.*;
import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//创建对象,连接服务端Socket socket = new Socket("127.0.0.1",10000);//写入通道数据String str = "见到你很高兴!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//添加写入通道数据的结束标志socket.shutdownOutput();//接受返回的数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while((b = isr.read()) != -1){System.out.print((char)b);}//释放资源socket.close();}
}
Server.java
package Test2;import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//创建服务端的对象ServerSocket ss = new ServerSocket(10000);//等待客户端连接Socket socket = ss.accept();//接受数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1) {System.out.print((char) b);}//返回数据String str = "高兴的不要不要的!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//释放资源ss.close();}
}
异常信息:
错误:Exception in thread "main" java.net.SocketException: Connection resetat java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:328)at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:355)at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:808)at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:270)at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:313)at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:188)at java.base/sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:137)at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:122)at java.base/java.io.InputStreamReader.read(InputStreamReader.java:169)at Test2.Client.main(Client.java:23)
我运行了代码,异常如下
如果熟悉java socket编程的人,肯定会一眼看出问题,是服务器关闭了,导致客户端继续获取数据而报的异常,而且数据流也没有关闭。
下面我把问题发给ChatGPT、讯飞星火、文心一言、通义千问、Kimi,看看他们的回答:
一:ChatGPT 3.5
这个错误通常表示服务器在尝试读取数据时,连接被重置了。这可能是因为服务器尝试读取数据时,客户端已经关闭了连接。要修复这个问题,你可以在客户端代码的写数据之后立即关闭输出流,而不是等待服务器端返回数据后再关闭连接。可以把
socket.shutdownOutput();
放在写数据之后,这样就不会触发服务器端的连接重置了。
二:讯飞星火
这个错误是因为在客户端和服务端的代码中,没有正确地关闭输入输出流和套接字。为了修复这个问题,你需要在每个代码块的末尾添加相应的关闭语句。以下是修复后的代码:
客户端代码:
package Test2;import java.io.*;
import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {//创建对象,连接服务端Socket socket = new Socket("127.0.0.1",10000);//写入通道数据String str = "见到你很高兴!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//添加写入通道数据的结束标志socket.shutdownOutput();//接受返回的数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while((b = isr.read()) != -1){System.out.print((char)b);}//释放资源isr.close();is.close();os.close();socket.close();}
}
服务端代码:
package Test2;import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {//创建服务端的对象ServerSocket ss = new ServerSocket(10000);//等待客户端连接Socket socket = ss.accept();//接受数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1) {System.out.print((char) b);}//返回数据String str = "高兴的不要不要的!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());//释放资源isr.close();is.close();os.close();socket.close();ss.close();}
}
三:通义千问
这个代码中,服务器端(Server)和客户端(Client)的通信逻辑存在一些问题,导致了“java.net.SocketException: Connection reset”异常。当客户端发送消息后立即关闭输出流并执行
socket.shutdownOutput()
,而服务器端还在读取输入流,没有及时响应和关闭连接,此时客户端可能已经关闭了连接,从而导致服务器端在读取时抛出连接重置异常。修复方案如下:
首先,我们需要确保客户端在接收服务器回复后再关闭连接。同时,为了避免服务器端在未发送完数据时就关闭连接,我们应在服务器端发送完数据后再关闭相关资源。
修改后的客户端(Client.java)代码:
package Test2;import java.io.*;
import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {// 创建对象,连接服务端Socket socket = new Socket("127.0.0.1", 10000);// 写入通道数据String str = "见到你很高兴!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());// 添加写入通道数据的结束标志(这里不需要调用 shutdownOutput,因为后面还要接收服务器的响应)// socket.shutdownOutput();// 接受返回的数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;StringBuilder sb = new StringBuilder();while ((b = isr.read()) != -1) {sb.append((char) b);}System.out.println(sb.toString());// 释放资源socket.close();}
}
修改后的服务器端(Server.java)代码:
package Test2;import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {// 创建服务端的对象ServerSocket ss = new ServerSocket(10000);// 等待客户端连接Socket socket = ss.accept();// 接受数据InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;StringBuilder sb = new StringBuilder();while ((b = isr.read()) != -1) {sb.append((char) b);}System.out.println(sb.toString());// 返回数据String str = "高兴的不要不要的!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());// 确保数据已发送完毕os.flush();// 释放资源socket.close();ss.close();}
}
四:文心一言
这个错误通常发生在客户端尝试从已经关闭的socket读取数据时。在你的代码中,问题出现在服务器端在读取完客户端发送的数据之后,就关闭了
ServerSocket
(ss.close();
),而不是客户端的socket(socket.close();
)。关闭ServerSocket
意味着服务器停止了监听新的连接,而当前已经建立的连接(socket
)并不会自动关闭。由于服务器在发送数据给客户端之前没有关闭它自己的socket输出流,客户端可以继续读取数据。但是,由于服务器关闭了
ServerSocket
(这是不恰当的),并且没有适当地关闭与客户端的连接(即没有调用socket.close();
),客户端在读取完数据后尝试继续读取时,可能会遇到连接已经被服务器重置的情况,从而抛出SocketException: Connection reset
异常。修复这个问题的方法是,在服务器端发送完数据给客户端之后,应该关闭与客户端的连接(即调用
socket.close();
),而不是关闭ServerSocket
。另外,服务器端在读取完客户端数据后,可能也想要发送一个明确的结束标记给客户端,以便客户端知道何时停止读取数据(尽管在这个例子中,由于你使用的是TCP,客户端可以检测到EOF(End Of File)来知道何时停止读取)。
下面是修复后的服务器端代码:
package Test2; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { // 创建服务端的对象 ServerSocket ss = new ServerSocket(10000); // 等待客户端连接 Socket socket = ss.accept(); // 接受数据 InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); int b; StringBuilder sb = new StringBuilder(); while ((b = isr.read()) != -1) { sb.append((char) b); } System.out.println("Received from client: " + sb.toString()); // 返回数据 String str = "高兴的不要不要的!!!"; OutputStream os = socket.getOutputStream(); os.write(str.getBytes()); os.flush(); // 确保数据被发送出去 // 释放资源:关闭与客户端的连接 socket.close(); // 注意这里关闭的是socket,不是ss // 当不再需要监听新的连接时,再关闭ServerSocket // 在这个例子中,因为我们只处理一个连接,所以可以立即关闭它 ss.close(); }
}
注意,os.flush();
这一行确保了所有待发送的数据都已经被发送到网络上。在你的原始代码中,虽然这不是导致SocketException
的直接原因,但它是良好的编程习惯。
此外,我添加了一个StringBuilder
来累积从客户端接收到的数据,并在读取完成后打印出来,以便于调试和查看接收到的数据。在实际应用中,你可能需要根据你的需求来处理这些数据。
五:Kimi
这段代码中的错误提示是
java.net.SocketException: Connection reset
,这通常意味着另一端的连接被关闭了。在这段代码中,有几个地方可能导致这个问题:
在
Client
类中,你调用了socket.shutdownOutput()
,这将关闭输出流,但它也会向对方发送一个结束连接的信号。然而,你的Server
类并没有正确处理这个信号,它仍然尝试读取数据,这将导致SocketException
。在
Server
类中,你创建了ServerSocket
和Socket
,但是没有关闭ServerSocket
。通常,你应该在完成通信后关闭它们。在
Server
类中,你只在读取完输入流后发送了响应,但是没有检查是否读取完毕,这可能导致客户端在服务端发送响应之前就关闭了连接。在
Client
类中,你在读取完服务端的响应后关闭了socket
,但是没有检查是否读取完毕。为了修复这个问题,你可以做以下修改:
- 在
Client
类中,不要立即关闭socket
,而是在读取完所有响应后再关闭。- 在
Server
类中,发送完响应后再关闭socket
和ServerSocket
。
下面是修复后的代码示例:
// Client 类保持不变// Server 类
package Test2;import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10000);Socket socket = ss.accept();InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while ((b = isr.read()) != -1) {System.out.print((char) b);}// 返回数据String str = "高兴的不要不要的!!!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());os.flush(); // 确保发送所有数据// 释放资源os.close();isr.close();is.close();socket.close();ss.close();}
}
请注意,我在 Server
类中添加了 os.flush()
来确保所有数据都被发送。同时,我按照正确的顺序关闭了流和套接字。
此外,如果你希望 Client
类在读取完所有数据后再关闭连接,你可以在读取完输入流后添加一个检查,确保已经读取完毕,然后再关闭 socket
。例如:
// Client 类
// ...while((b = isr.read()) != -1){System.out.print((char)b);
}// 确保所有数据都已读取
isr.ready();// 释放资源
isr.close();
is.close();
os.close();
socket.close();
// ...
请根据你的具体需求调整上述代码。
-----------------以上就是各种GPT的回答-----------------
因此,好的编程习惯也很重要,各个GPT都表现的非常好,不过还是有一些问题。
1.ChatGPT 3.5和通义千问对于socket.shutdownOutput();的解答是错的,和这句话没有关系
2.
讯飞星火和Kimi的回答是最好的,只是对异常只是抛出处理,不过对于解决普通问题够用了。
3.文心一言只把目光聚焦在解决单一问题上,没有对数据流的关闭做处理。
大家觉得哪一个更厉害呢?