从理论到实践网络编程模型:(BIO、NIO、AIO)同步与异步模型的原理与应用 (六)

序接上回

在上一节中,我们展示了一个简单的客户端-服务器聊天程序的示例代码。尽管该程序能够实现基本的消息发送和接收功能,但在实际生产环境中,需求往往远不止于此。为了支持多个客户端并发交互,我们需要改进现有的设计,使用 BIO(Blocking I/O)模型 来处理多个用户的聊天信息。以下将详细讨论这一改进方案。

现有代码的局限性

上述代码在处理多个客户端时存在几个主要问题:

  1. 单线程处理:当前的服务器代码是单线程的,这意味着只能同时处理一个客户端的请求。当一个客户端连接并发送消息时,服务器将被阻塞,直到该客户端断开连接或发送 "quit"。

  2. 阻塞问题:在等待输入时,服务器会被阻塞,无法响应其他客户端的请求,这导致了潜在的性能瓶颈。

  3. 扩展性不足:随着连接的增加,服务器的响应时间可能会显著增加,不能满足高并发请求的需求。

基于 BIO 的多人聊天室的实现

为了实现一个可与多个客户端并发交互的聊天室,我们需要为每个连接创建一个新的线程。这样,每个线程可以独立地处理来自不同用户的信息,而不会相互阻塞。

以下是改进后的服务器代码:

java 复制代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Main {
    public static void main(String[] args) {
        final String QUIT = "quit";
        final int DEFAULT_PORT = 7000;

        try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
            System.out.println("服务器启动,监听端口 " + DEFAULT_PORT);

            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println("客户端 " + socket.getPort() + " 连接成功");
                new Thread(new ClientHandler(socket)).start(); // 为每个客户端启动一个新线程
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ClientHandler implements Runnable {
    private Socket socket;

    public ClientHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        final String QUIT = "quit";
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {

            String msg;
            while ((msg = reader.readLine()) != null) {
                System.out.println("收到客户端 " + socket.getPort() + " 消息: " + msg);
                writer.write("服务器收到消息: " + msg + "\n");
                writer.flush();
                if (QUIT.equals(msg)) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
                System.out.println("客户端 " + socket.getPort() + " 连接关闭");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

代码解释

  1. 多线程处理 :代码中的 ClientHandler 类实现了 Runnable 接口,每当有新的客户端连接时,主线程会创建一个新的 ClientHandler 实例并启动一个新线程来处理该连接。

  2. 独立处理:每个线程独立处理输入和输出,不会阻塞其他连接。这意味着即使一个客户端在发送消息,其他客户端仍然可以与服务器进行交互。

  3. 资源管理 :在 run 方法中,使用 try-with-resources 确保在处理完成后自动关闭输入输出流,简化了资源管理。

流程图

以下流程图展示了如何使用 BIO 模型处理多个用户的聊天信息,同时考虑到服务器与客户端之间的交互:
发送信息 处理信息 处理用户A的消息 处理用户B的消息 处理用户C的消息 发送消息给每个客户端 等待用户输入 继续接收输入 用户输入聊天信息 服务器接收信息 创建线程处理各个连接 线程处理 将消息广播给其他用户 所有客户端接收信息 阻塞等待

中文解释

  1. 用户输入聊天信息:用户在客户端输入消息。
  2. 发送信息:客户端将信息发送到服务器。
  3. 服务器接收信息:服务器接收到用户的信息。
  4. 处理信息:服务器对接收到的信息进行初步处理。
  5. 创建线程处理各个连接:为每个连接创建一个新的线程,以便并行处理多个用户的请求。
  6. 线程处理 :每个线程独立处理不同用户的消息。
    • 处理用户A的消息:比如线程专门处理用户A发来的消息。
    • 处理用户B的消息:同样,处理用户B的消息。
    • 处理用户C的消息:处理用户C的消息。
  7. 将消息广播给其他用户:处理完消息后,服务器将其广播给所有其他在线用户。
  8. 所有客户端接收信息:每个客户端接收服务器发送的消息。
  9. 等待用户输入:客户端等待用户输入。
  10. 阻塞等待:在等待用户输入期间,可能会有阻塞。
  11. 继续接收输入:一旦有新的输入,流程再次回到用户输入聊天信息的步骤。

通过将聊天服务器改为多线程模型,我们可以有效地提高其并发处理能力,满足多用户同时进行交互的需求。BIO 模型虽然在某些场景下效率有限,但在小规模的聊天应用中依然是一个有效的解决方案。对于更复杂的需求,未来可以考虑实现 NIO(非阻塞 I/O)或使用 WebSocket 等更先进的技术,一步一步来。万变不离其宗,io服务器和客户端的交互,做一个小型的客服是足够的,实际生产中是需求产生代码,学习的时候想法驱动代码,加油

持续更新中~~

相关推荐
xlsw_1 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹2 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石3 小时前
12/21java基础
java
李小白663 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
fantasy_arch4 小时前
CPU性能优化-磁盘空间和解析时间
网络·性能优化