一、初步了解
1、简易代码(存在socket提前关闭问题)
服务端代码:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;public class MySocketServer {public static void main(String[] args) throws IOException {// 创建一个serverSocket,监听一个端口号并创建通信socketServerSocket serverSocket = new ServerSocket(8888);// 当有客户端连接时创建一个通信socket,没有连接时会阻塞Socket socket = serverSocket.accept();// 打印客户端信息System.out.println("客户端" + socket.getInetAddress().getLocalHost() + "连接到服务器");// 输入流,用于读取客户端信息InputStream in = socket.getInputStream();// 良好习惯,关闭输出流in.close();// 输出流,用于给客户端返回信息OutputStream out = socket.getOutputStream();/* 读取客户端信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 打印从客户端收到的信息System.out.println("服务端接收到消息:" + msgBuilder.toString());// 给客户端返回信息out.write(("服务端收到消息:" + msgBuilder.toString()).getBytes(StandardCharsets.UTF_8) );// 输出缓冲数据out.flush();// 关闭输出流out.close();}
}
客户端代码
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Date;public class MySocketClient {public static void main(String[] args) throws IOException {// 指定ip和端口号,创建socket连接Socket socket = new Socket("127.0.0.1", 8888);/* 发送消息 */String msg = "客户端发送了一条消息,现在是北京时间" + new Date();InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();out.write(msg.getBytes(StandardCharsets.UTF_8));out.flush();// 关闭输出流out.close();/* 读取信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 打印收到信息System.out.println("客户端收到服务端回信:" + msgBuilder);// 关闭输入流in.close();}
}
运行结果
1、步骤:先启动服务端,再启动客户端
2、服务端打印:
客户端LAPTOP-EECN3AOI/192.168.31.39连接到服务器
Exception in thread "main" java.net.SocketException: Socket is closedat java.net.Socket.getOutputStream(Socket.java:943)at MySocketServer.main(MySocketServer.java:19)
3、客户端打印:
Exception in thread "main" java.net.SocketException: socket closedat java.net.SocketInputStream.socketRead0(Native Method)at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)at java.net.SocketInputStream.read(SocketInputStream.java:171)at java.net.SocketInputStream.read(SocketInputStream.java:141)at java.net.SocketInputStream.read(SocketInputStream.java:127)at MySocketClient.main(MySocketClient.java:24)
4、错误分析:socket被关闭
5、原因:分析代码,未对socket进行关闭,但是客户端在通信完成前提前关闭了out流,服务端提前关闭了in流,查询资料得知关闭流会导致socket关闭
2、将所有关闭流操作去掉,存在死锁问题
服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;public class MySocketServer {public static void main(String[] args) throws IOException {// 创建一个serverSocket,监听一个端口号并创建通信socketServerSocket serverSocket = new ServerSocket(8888);// 当有客户端连接时创建一个通信socket,没有连接时会阻塞Socket socket = serverSocket.accept();// 打印客户端信息System.out.println("客户端" + socket.getInetAddress().getLocalHost() + "连接到服务器");// 输入流,用于读取客户端信息InputStream in = socket.getInputStream();// 输出流,用于给客户端返回信息OutputStream out = socket.getOutputStream();/* 读取客户端信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 打印从客户端收到的信息System.out.println("服务端接收到消息:" + msgBuilder.toString());// 给客户端返回信息out.write(("服务端收到消息:" + msgBuilder.toString()).getBytes(StandardCharsets.UTF_8) );}
}
客户端
public class MySocketClient {public static void main(String[] args) throws IOException {// 指定ip和端口号,创建socket连接Socket socket = new Socket("127.0.0.1", 8888);/* 发送消息 */String msg = "客户端发送了一条消息,现在是北京时间" + new Date();InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();out.write(msg.getBytes(StandardCharsets.UTF_8));out.flush();/* 读取信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 打印收到信息System.out.println("客户端收到服务端回信:" + msgBuilder);}
}
运行结果
1、服务端:打印连接信息后无响应
客户端LAPTOP-EECN3AOI/192.168.31.39连接到服务器
2、客户端:无响应
3、原因:分析代码,服务端没有打印出客户端发送消息,猜测是以下代码陷入死循环。
byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}
查资料得知,在普通流当中,这个方法可行。但是在socket中,只有当对方将输出流关闭后才会以-1作为结束标志,故而陷入死循环,推断正确。
4、解决方法:需要关闭流的同时,不关闭socket,可使用Socket::shutdownOutput()方法和Socket::shutdownInput()方法实现
3、使用正确方法关闭流,通信成功
服务端
public class MySocketServer {public static void main(String[] args) throws IOException {// 创建一个serverSocket,监听一个端口号并创建通信socketServerSocket serverSocket = new ServerSocket(8888);// 当有客户端连接时创建一个通信socket,没有连接时会阻塞Socket socket = serverSocket.accept();// 打印客户端信息System.out.println("客户端" + socket.getInetAddress().getLocalHost() + "连接到服务器");// 输入流,用于读取客户端信息InputStream in = socket.getInputStream();// 输出流,用于给客户端返回信息OutputStream out = socket.getOutputStream();/* 读取客户端信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 关闭输入流socket.shutdownInput();// 打印从客户端收到的信息System.out.println("服务端接收到消息:" + msgBuilder.toString());// 给客户端返回信息out.write(("服务端收到消息:" + msgBuilder.toString()).getBytes(StandardCharsets.UTF_8) );// 关闭输出流同时关闭socketout.close();}
}
客户端
public class MySocketClient {public static void main(String[] args) throws IOException {// 指定ip和端口号,创建socket连接Socket socket = new Socket("127.0.0.1", 8888);/* 发送消息 */String msg = "客户端发送了一条消息,现在是北京时间" + new Date();InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();out.write(msg.getBytes(StandardCharsets.UTF_8));out.flush();// 关闭输出流socket.shutdownOutput();/* 读取信息 */byte[] buffer = new byte[1024];int len;StringBuilder msgBuilder = new StringBuilder();while ((len = in.read(buffer)) != -1) {msgBuilder.append(new String(buffer, 0, len));}// 打印收到信息System.out.println("客户端收到服务端回信:" + msgBuilder);// 关闭输入流同时关闭socketin.close();}
}
输出结果
1、服务端
客户端LAPTOP-EECN3AOI/192.168.31.39连接到服务器
服务端接收到消息:客户端发送了一条消息,现在是北京时间Sun Jan 07 16:46:26 GMT+08:00 2024
2、客户端
客户端收到服务端回信:服务端收到消息:客户端发送了一条消息,现在是北京时间Sun Jan 07 16:46:26 GMT+08:00 2024