【Java】- TCP、UDP协议实现简易聊天室

本文介绍了如何使用Java实现基于TCP和UDP的简易聊天室,包括数据设计、消息转发与接收的原理。核心是通过服务器将一个客户端的消息转发给其他所有客户端,涉及多线程和异步操作。在服务器状态与性能部分,讨论了随着客户端数量增加,服务器CPU、内存和线程的占用情况,并提出了优化策略,如减少线程数量和复用客户端Buffer。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据设计

必要条件:客户端、服务器端

必要约束:数据传输协议,使用尾部追加换行结束符

原理:服务器端监听消息来源,客户端连接服务器并发送消息到服务器。客户端-服务器-转发到客户端。

多类型客户端:一客户端发送消息给服务器端,服务器端将该消息转发给其他客户端,实现客户端之间的通信。

核心是数据转发协议。

消息转发与接收

在这里插入图片描述
聊天室的内容就是在UDP+TCP双向通信的基础上改成了多个客户端,所以基本内容与【Socket网络编程】-UDP辅助TCP实现点到点传输Java一样,只不过改成了子模块的结构,然后添加依赖。

客户端没有任何改动,主要是把服务器端的信息打印转换为将消息转发给其他客户端,需要要到异步操作,所以跟多线程有关。

TCPServer

import handle.ClientHandler;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 * Created by 007 on 2020/7/12.
 */
public class TCPServer implements ClientHandler.ClientHandlerCallback {

    private final int port;
    private ClientListener mListener;
    private List<ClientHandler> clientHandlerList = new ArrayList<>();//线程安全问题
    private final ExecutorService forwardingThreadPoolExecutor;//消息转发构建异步线程池-单例

    public TCPServer(int port) {
        this.port = port;
        //转发线程池
        this.forwardingThreadPoolExecutor = Executors.newSingleThreadExecutor();

    }

    public boolean start() {
        try {
            ClientListener listener = new ClientListener(port);
            mListener = listener;
            listener.start();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public void stop() {
        if (mListener != null) {
            mListener.exit();
        }

        //使用同步块解决线程安全问题
        synchronized (TCPServer.this) {
            //把每个客户端都退出一下
            for (ClientHandler clientHandler : clientHandlerList) {
                clientHandler.exit();
            }
            //清空客户端列表
            clientHandlerList.clear();
        }
        //停止线程池
        forwardingThreadPoolExecutor.shutdownNow();

    }


    public void broadcast(String str) {
        for (ClientHandler clientHandler : clientHandlerList) {
            clientHandler.send(str);
        }

    }

    @Override
    public synchronized void onSelfClosed(ClientHandler handler) {//加在方法上的同步块默认是当前实例TCPServer
        clientHandlerList.remove(handler);
    }

    @Override
    public void onNewMessageArrived(final ClientHandler handler, final String msg) {
        //打印到屏幕上
        System.out.println("Received-" + handler.getClientInfo() + ":" + msg);

        //异步提交转发任务
        forwardingThreadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                synchronized (TCPServer.this) {
                    for (ClientHandler clientHandler : clientHandlerList) {
                        if (clientHandler.equals(handler)) {
                            //跳过自己,不给自己转发消息
                            continue;
                        }
                        //对其他客户端发送消息
                        clientHandler.send(msg);
                    }
                }
            }
        });

    }


    public class ClientListener extends Thread {
        private ServerSocket server;
        private boolean done = false;

        private ClientListener(int port) throws IOException {
            server = new ServerSocket(port);
            System.out.println("服务器信息:" + server.getInetAddress() + ",P:" + server.getLocalPort());
        }

        @Override
        public void run() {
            super.run();

            System.out.println("服务器准备就绪~");
            //等待客户端连接
            do {
                //得到客户端
                Socket client;
                try {
                    client = server.accept();
                } catch (IOException e) {
                    continue;
                }
                try {
                    //客户端构建异步线程
                    ClientHandler clientHandler = new ClientHandler(client, TCPServer.this);
                    //读取数据并打印
                    clientHandler.readToPrint();
                    //添加同步处理,也是同步块解决线程
                    synchronized (TCPServer.this) {
                        clientHandlerList.add(clien
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值