Java socket编程学习笔记

2024-01-07 17:29:18

一、初步了解

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,监听一个端口号并创建通信socket
        ServerSocket 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 closed
	at java.net.Socket.getOutputStream(Socket.java:943)
	at MySocketServer.main(MySocketServer.java:19)

?3、客户端打印:

Exception in thread "main" java.net.SocketException: socket closed
	at 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,监听一个端口号并创建通信socket
        ServerSocket 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,监听一个端口号并创建通信socket
        ServerSocket 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) );
        // 关闭输出流同时关闭socket
        out.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);
        // 关闭输入流同时关闭socket
        in.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

4、心得

(1)关闭socket的 输入/输出流 时,会将socket连接一起关闭,当socket还需要继续工作时,需要使用内置方法shutdownOutput和shutdownInput关闭流
(2)输入流只有当对方输出流关闭时,才会以-1作为标志

文章来源:https://blog.csdn.net/weixin_48460141/article/details/135441201
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。