聊天室极简实现

从今天起学习新知识了,再也不是复习老知识了。。

聊天室极简实现,要求:

  1. 有群聊和私聊两种模式;
  2. 当用户进入聊天室时,自己的视角显示“欢迎您进入聊天室”,聊天室其他成员显示“XXX进入聊天室”;
  3. 私聊模式中规定信息格式:@XXX:msg。为了好实现。

码代码前分析:

  1. 客户端需要开辟两个线程分别接收消息和发送消息;
  2. 服务器端需要不断监听端口是否被新的客户端连接,若有新客户端连接则开辟新线程负责该客户端与服务器间的通信,由此实现个客户端不影响不会阻塞。

具体代码实现:

  • 先写一个工具类,用来关闭资源:方法参数为可变参数
public class ChatUtils {
    public static void release(Closeable... targets) {
        try {
            for (Closeable target : targets) {
                if (target != null) {
                    target.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 客户端代码:
public class Client {

    public static void main(String[] args) throws IOException {
        System.out.println("———————客户端——————");
        System.out.println("请输入用户名:");
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String name = bufferedReader.readLine();

        //建立连接:使用socket建立客户端+服务的地址和端口
        Socket client = new Socket("localhost", 8888);
        System.out.println("欢迎进入聊天室!");

        //发送消息和接受消息分别创建两个线程互不打扰
        new Thread(new Send(client, name)).start();
        new Thread(new Receive(client)).start();
    }

    /**
     * 从控制台读后再发送到服务端
     */
    static class Send implements Runnable {

        private BufferedReader br; //从控制台读
        private DataOutputStream dos; //发到服务器端
        private Socket client;
        private boolean flag = true;
        private String name; //用户名

        Send(Socket client, String name) {
            this.client = client;
            this.name = name;
            this.br = new BufferedReader(new InputStreamReader(System.in));
            try {
                this.dos = new DataOutputStream(client.getOutputStream());
                dos.writeUTF(name);//在构造函数中将用户名发出去
                dos.flush();
            } catch (IOException e) {
                flag = false;
                ChatUtils.release(br, dos, client);
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            while (flag) { //不断从控制台读取后发送
                try {
                    String str = br.readLine();
                    dos.writeUTF(str);
                    dos.flush();
                } catch (IOException e) {
                    flag = false;
                    ChatUtils.release(br, dos, client);
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从服务端读消息
     */
    static class Receive implements Runnable {

        private DataInputStream dis;
        private Socket client;
        private boolean flag = true;

        Receive(Socket client) {
            this.client = client;
            try {
                this.dis = new DataInputStream(client.getInputStream());
            } catch (IOException e) {
                flag = false;
                ChatUtils.release(dis, client);
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            while (flag) { //不断从服务端收取消息
                try {
                    System.out.println(dis.readUTF());
                } catch (IOException e) {
                    flag = false;
                    ChatUtils.release(dis, client);
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 服务端代码:
public class Server {

    static CopyOnWriteArrayList<Channel> list = new CopyOnWriteArrayList<>(); //装连接的容器,需要并发读写

    public static void main(String[] args) throws IOException {
        System.out.println("———————服务端——————");

        //指定端口 使用serversocket创建服务器
        ServerSocket server = new ServerSocket(8888);

        //服务器不断扫描是否有新客户端连接,若有则开辟新线程
        while (true) {
            Socket client = server.accept(); //阻塞式等待连接 accept
            Channel channel = new Channel(client);
            list.add(channel);
            System.out.println("客户端:" + channel.name + ",已连接!");
            new Thread(channel).start();
        }
    }

    static class Channel implements Runnable {

        private Socket client;
        private DataInputStream dis;
        private DataOutputStream dos;
        private Boolean flag = true;
        String name; //客户端名称

        Channel(Socket client) {
            this.client = client;
            try {
                this.dis = new DataInputStream(client.getInputStream());
                this.dos = new DataOutputStream(client.getOutputStream());
                this.name = receive(); //从客户端传来名称,注意这一步要在构造函数中完成
                sendOthers(this.name, true); //有客户端进入房间后要发出系统消息通知其他客户端
            } catch (IOException e) {
                flag = false;
                ChatUtils.release(dis, dos, client); //释放连接
                e.printStackTrace();
            }
        }

        /**
         * 不断读取当前客户端发送的消息发送给其他连接的客户端
         */
        @Override
        public void run() {
            while (flag) {
                String msg = receive();
                if (!msg.equals("")) {
                    sendOthers(msg, false); //此时非系统消息
                }
            }
        }

        /**
         * 发送消息给当前的客户端
         *
         * @param str
         */
        void send(String str) {
            try {
                dos.writeUTF(str);
                dos.flush();
            } catch (IOException e) {
                flag = false;
                ChatUtils.release(dis, dos, client);
                e.printStackTrace();
            }
        }

        /**
         * 接受当前客户端的消息
         *
         * @return
         */
        String receive() {
            String str = "";
            try {
                str = dis.readUTF();
            } catch (IOException e) {
                flag = false;
                ChatUtils.release(dis, dos, client);
                e.printStackTrace();
            }
            return str;
        }

        /**
         * 转发给其他服务器,有两种模式:私聊模式与群聊模式。
         * 规定私聊模式的格式为:@XXX:msg。
         * 群聊模式中分系统消息和对话消息。
         *
         * @param str
         */
        void sendOthers(String str, boolean isSys) {
            boolean isPrivate = str.startsWith("@");
            if (isPrivate) { //私聊模式
                int index = str.indexOf(":");
                String toName = str.substring(1, index); //找到对方名字
                String msg = str.substring(index + 1); //分离出消息
                for (Channel channel : list) {  //找到客户端私聊
                    if (channel.name.equals(toName)) {
                        channel.send(this.name + "对您说:" + msg);
                        break;
                    }
                }
            } else { //群聊模式
                for (Channel channel : list) {
                    if (channel == this) { //无需发给自己
                        continue;
                    }
                    if (isSys) { //系统消息
                        channel.send("用户:" + this.name + ",进入聊天室");
                    } else { //对话消息
                        channel.send(this.name + "对所有人说:" + str);
                    }

                }
            }
        }
    }
}

导入的包就省略了,由此可以简易实现聊天室。

发布了41 篇原创文章 · 获赞 9 · 访问量 9838

猜你喜欢

转载自blog.csdn.net/Serena0814/article/details/105151753