Java 网络编程之TCP(二):基于BIO的聊天室

在上一篇【Java 网络编程之TCP(一):基于BIO】中,介绍Java中I/O和TCP的基本概念,本文在上文的基础上,实现一个基本的聊天室的功能。

聊天室需求描述:

聊天客户端:发送消息给所有其他客户端,接收其他客户端的消息

实现说明:

要想实现上面的聊天室的功能,我们需要一个服务端,和客户端

服务端:接收客户端的消息,并转发给其他客户端

客户端:发送消息给服务端,接收服务端的消息

由于基于BIO,那么服务端都需要用两个线程,来分别进行收发消息

代码如下:

服务端:

java 复制代码
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;

/**
 * 基于BIO的TCP网络通信的服务端,可以接收多个客户端连接,通过字节流接收客户端发送的消息;
 * 并发每个客户端的数据,转发给其余所有的客户端
 * 一个客户端需要使用一个线程
 * todo:线程资源复用; 字符流readLine 没有换行符阻塞的问题
 *
 * @author freddy
 */
class ChatServer {
    // 存放所有和服务端建立连接的客户端,客户端断开,需要去除
    public static List<Socket> clients = new LinkedList<>();

    public static void main(String[] args) throws IOException {
        // 开启server 监听端口
        ServerSocket serverSocket = new ServerSocket(9999);
        while (true) {
            Socket client = serverSocket.accept(); // 阻塞操作,需要新的线程处理客户端
            // 客户端保存,方便后面转发数据
            clients.add(client);
            // 接收Client数据,并转发
            new Thread(new ServerThread(client, clients)).start();
        }
    }
}

/**
 * 服务端的线程,一个客户端对应一个
 */
class ServerThread implements Runnable {
    Socket socket;

    List<Socket> clients;

    public ServerThread(Socket socket, List<Socket> clients) {
        this.socket = socket;
        this.clients = clients;
    }

    @Override
    public void run() {
        // 获取输入流程,读取用户输入
        // 持续接收Client数据,并打印
        System.out.println("server had a client" + socket);
        try (InputStream inputStream = socket.getInputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            // read操作阻塞,直到有数据可读
            // -1 表示流关闭,或者读到文件末尾
            while ((len = inputStream.read(buffer)) != -1) {
                System.out.println("serer receive data from " + socket + " : " + new String(buffer, 0, len));
                // 转发数据到其他Client
                for (Socket client : clients) {
                    if (client != socket) {
                        client.getOutputStream().write(new String(buffer, 0, len).getBytes());
                    }
                }
            }
        } catch (IOException e) {
            System.out.println(socket + " disconnect ");
            // 去除当前client
            clients.remove(socket);
        }
    }
}

客户端代码:

java 复制代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * 基于BIO的TCP网络通信的客户端,接收控制台输入的数据,然后通过字节流发送给服务端
 *
 * @author freddy
 */
class ChatClient {
    public static void main(String[] args) throws IOException {
        // 连接server
        Socket serverSocket = new Socket("localhost", 9999);
        System.out.println("client connected to server");

        // 读取用户在控制台上的输入,并发送给服务器
        new Thread(new ClientThread(serverSocket)).start();

        // 接收服务端发送过来的数据
        try (InputStream serverSocketInputStream = serverSocket.getInputStream();) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = serverSocketInputStream.read(buffer)) != -1) {
                System.out.println(
                    "client receive data from server" + serverSocketInputStream + " : " + new String(buffer, 0, len));
            }
        }

    }
}

class ClientThread implements Runnable {
    private Socket serverSocket;

    public ClientThread(Socket serverSocket) {
        this.serverSocket = serverSocket;
    }

    @Override
    public void run() {
        // 读取用户在控制台上的输入,并发送给服务器
        InputStream in = System.in;
        byte[] buffer = new byte[1024];
        int len;
        try (OutputStream outputStream = serverSocket.getOutputStream();) {
            // read操作阻塞,直到有数据可读,由于后面还要接收服务端转发过来的数据,这两个操作都是阻塞的,所以需要两个线程
            while ((len = in.read(buffer)) != -1) {
                System.out.println("client receive data from console" + in + " : " + new String(buffer, 0, len));
                // 发送数据给服务器端
                outputStream.write(new String(buffer, 0, len).getBytes()); // 此时buffer中是有换行符
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

测试:

先开启服务端,再开启两个客户端,在客户端1的控制台发送i'm client1,在客户端1的控制台发送this is client2,通过日志可以看到,聊天室的功能符合要求:

相关推荐
KingDol_MIni15 分钟前
Spring Boot 集成 T-io 实现客户端服务器通信
java·服务器·spring boot
许苑向上19 分钟前
Java八股文(下)
java·开发语言
逸Y 仙X23 分钟前
Git常见命令--助力开发
java·大数据·git·java-ee·github·idea
独孤求败Ace27 分钟前
第44天:Web开发-JavaEE应用&反射机制&类加载器&利用链&成员变量&构造方法&抽象方法
java·开发语言
FLZJ_KL27 分钟前
【设计模式】【创建型模式】单例模式(Singleton)
java·单例模式·设计模式
CL_IN35 分钟前
企业数据集成:实现高效调拨出库自动化
java·前端·自动化
计算机-秋大田40 分钟前
基于Spring Boot的农产品智慧物流系统设计与实现(LW+源码+讲解)
java·开发语言·spring boot·后端·spring·课程设计
计算机毕设指导61 小时前
基于SpringBoot的城乡商城协作系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven
华子w9089258591 小时前
基于数据可视化+SpringBoot+安卓端的数字化施工项目计划与管理平台设计和实现
java·spring boot·后端
橘猫云计算机设计1 小时前
基于Django的购物商城平台的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·数据库·spring boot·后端·django