从理论到实践网络编程模型:(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服务器和客户端的交互,做一个小型的客服是足够的,实际生产中是需求产生代码,学习的时候想法驱动代码,加油

持续更新中~~

相关推荐
Allen Bright1 分钟前
maven概述
java·maven
编程重生之路3 分钟前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱4 分钟前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea
努力进修13 分钟前
“探索Java List的无限可能:从基础到高级应用“
java·开发语言·list
politeboy13 分钟前
k8s启动springboot容器的时候,显示找不到application.yml文件
java·spring boot·kubernetes
刽子手发艺14 分钟前
WebSocket详解、WebSocket入门案例
网络·websocket·网络协议
Daniel 大东1 小时前
BugJson因为json格式问题OOM怎么办
java·安全
速盾cdn4 小时前
速盾:CDN是否支持屏蔽IP?
网络·网络协议·tcp/ip
yaoxin5211234 小时前
第二十七章 TCP 客户端 服务器通信 - 连接管理
服务器·网络·tcp/ip