Socket粘包问题的3种解决方案,最后一种最完美!(4)

简介: Socket粘包问题的3种解决方案,最后一种最完美!(4)

优缺点分析


从以上代码可以看出,虽然这种方式可以解决粘包和半包的问题,但这种固定缓冲区大小的方式增加了不必要的数据传输,因为这种方式当发送的数据比较小时会使用空字符来弥补,所以这种方式就大大的增加了网络传输的负担,所以它也不是最佳的解决方案。


解决方案二:封装请求协议


这种解决方案的实现思路是将请求的数据封装为两部分:数据头+数据正文,在数据头中存储数据正文的大小,当读取的数据小于数据头中的大小时,继续读取数据,直到读取的数据长度等于数据头中的长度时才停止。


因为这种方式可以拿到数据的边界,所以也不会导致粘包和半包的问题,但这种实现方式的编码成本较大也不够优雅,因此不是最佳的实现方案,因此我们这里就略过,直接来看最终的解决方案吧。


解决方案三:特殊字符结尾,按行读取


以特殊字符结尾就可以知道流的边界了,因此也可以用来解决粘包和半包的问题,此实现方案是我们推荐最终解决方案


这种解决方案的核心是,使用 Java 中自带的 BufferedReaderBufferedWriter,也就是带缓冲区的输入字符流和输出字符流,通过写入的时候加上 \n 来结尾,读取的时候使用 readLine 按行来读取数据,这样就知道流的边界了,从而解决了粘包和半包的问题。


服务器端实现代码如下:


/**
 * 服务器端,改进版三(只负责收消息)
 */
static class ServSocketV3 {
    public static void main(String[] args) throws IOException {
        // 创建 Socket 服务器端
        ServerSocket serverSocket = new ServerSocket(9092);
        // 获取客户端连接
        Socket clientSocket = serverSocket.accept();
        // 使用线程池处理更多的客户端
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(100, 150, 100,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));
        threadPool.submit(() -> {
            // 消息处理
            processMessage(clientSocket);
        });
    }
    /**
     * 消息处理
     * @param clientSocket
     */
    private static void processMessage(Socket clientSocket) {
        // 获取客户端发送的消息流对象
        try (BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()))) {
            while (true) {
                // 按行读取客户端发送的消息
                String msg = bufferedReader.readLine();
                if (msg != null) {
                    // 成功接收到客户端的消息并打印
                    System.out.println("接收到客户端的信息:" + msg);
                }
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}


PS:上述代码使用了线程池来解决多个客户端同时访问服务器端的问题,从而实现了一对多的服务器响应。


客户端的实现代码如下:


/**
 * 客户端,改进版三(只负责发送消息)
 */
static class ClientSocketV3 {
    public static void main(String[] args) throws IOException {
        // 启动 Socket 并尝试连接服务器
        Socket socket = new Socket("127.0.0.1", 9092);
        final String message = "Hi,Java."; // 发送消息
        try (BufferedWriter bufferedWriter = new BufferedWriter(
                new OutputStreamWriter(socket.getOutputStream()))) {
            // 给服务器端发送 10 次消息
            for (int i = 0; i < 10; i++) {
                // 注意:结尾的 \n 不能省略,它表示按行写入
                bufferedWriter.write(message + "\n");
                // 刷新缓冲区(此步骤不能省略)
                bufferedWriter.flush();
            }
        }
    }
}


以上代码的执行结果如下图所示:


微信图片_20220120154103.jpg

相关文章
|
3月前
|
监控 网络协议 iOS开发
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
54 0
|
3月前
|
监控 网络协议 数据安全/隐私保护
手机socket套接字75秒超时问题及解决方案
手机socket套接字75秒超时问题及解决方案
38 0
|
3月前
|
XML JSON 网络协议
Socket实现模拟TCP通信粘包问题
Socket实现模拟TCP通信粘包问题
|
12月前
|
网络协议 网络安全
python-- socket 粘包、实现 ssh
python-- socket 粘包、实现 ssh
|
存储 缓存 网络协议
Socket 如何处理粘包
Socket 如何处理粘包
63 0
|
关系型数据库 MySQL
【技术分享】Can ‘t connect to local MySQL server through socket ‘/tmp/mysql.sock ‘解决方案
【技术分享】Can ‘t connect to local MySQL server through socket ‘/tmp/mysql.sock ‘解决方案
182 0
【技术分享】Can ‘t connect to local MySQL server through socket ‘/tmp/mysql.sock ‘解决方案
|
网络协议 图形学
Socket TCP协议解决粘包、半包问题的三种解决方案
Socket TCP协议解决粘包、半包问题的三种解决方案
274 1
Socket TCP协议解决粘包、半包问题的三种解决方案
|
存储 缓存 网络协议
Windows技术篇——Socket粘包问题
Windows技术篇——Socket粘包问题
248 0
Socket粘包问题终极解决方案—Netty版(2W字)!(11)
Socket粘包问题终极解决方案—Netty版(2W字)!(11)
121 0
Socket粘包问题终极解决方案—Netty版(2W字)!(11)
Socket粘包问题终极解决方案—Netty版(2W字)!(10)
Socket粘包问题终极解决方案—Netty版(2W字)!(10)
141 0