手写一个聊天室

手写一个聊天室
主要功能:
1.接收消息
2.发送消息
3.自动释放资源
4.多人聊天与私聊
涉及知识点:

  1. 多线程:runnable,thread
  2. 遍历容器:迭代器
  3. 基于TCP的简单编程
  4. IO流的对接操作
  5. 面向对象封装思想

利用封装的思想
1.封装服务对象(每个客户端(Channel)即一个对象)
*接收消息
*发送消息
*私聊与多人聊天
*借助工具类释放资源
2.封装一个工具类
*有一个释放资源的方法

多人聊天实现思路: 将所有客户端装入容器,在封装的sendOthers()方法,判断是否多人,若是遍历容器后将消息发送给除自己以外的其他所有人,使用send()方法。
私聊实现思路: 约定以 (@用户名:消息内容) 形式只对该用户发送消息,在封装的sendOthers()方法中判断是否私人,若是使用send()方法
容器选择:CopyOnWriteArrayList,可以同时读写。
工作方法:简单来说,就是平时查询的时候,都不需要加锁,随便访问,只有在写入/删除的时候,才会从原来的数据复制一个副本出来,然后修改这个副本,最后把原数据替换成当前的副本。修改操作的同时,读操作不会被阻塞,而是继续读取旧的数据。
共有五个文件

服务端

package com.lijing.chat05;

import com.lijing.chat05.lijingUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/*
在线聊天室:服务端
*/

public class Chat {
    private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<>();
    public static void main(String[] args) throws IOException {
        System.out.println("----------Server-----------");
        // 1、指定端口 使用ServerSocket创建服务器
        ServerSocket server = new ServerSocket(8888);
        //2、阻塞式等待连接 accept
        while(true){
            Socket client = server.accept();
            System.out.println("一个客户端建立了连接");
            Channel c = new Channel(client);
            all.add(c);//管理所有的成员
            new Thread(c).start();
        }
    }
    //一个客户代表一个Channel
    static class Channel implements Runnable{
        private DataInputStream dis;
        private DataOutputStream dos;
        private Socket client;
        private boolean isRunning;
        private String name;
        public Channel(Socket client){
            this.client = client;
            try {
                dis = new DataInputStream(client.getInputStream());
                dos = new DataOutputStream(client.getOutputStream());
                isRunning = true;
                //获取名称
                this.name =receive();
                //欢迎你的到来
                this.send("欢迎你的到来");
                sendOthers(this.name+"来到了聊天室",true);
            } catch (IOException e) {
                System.out.println("-------1-------");
                release();
            }
        }
        //接收消息
        private  String receive(){
            String msg="";
            try {
                msg = dis.readUTF();
            } catch (IOException e) {
                System.out.println("-------2-------");
                release();
            }
            return msg;
        }
        /*群聊: 获取自己的消息,发给其他人
          私聊: 约定数据格式:@xxx:msg
         */
        private void sendOthers(String msg,boolean isSys){
           boolean isPrivate = msg.startsWith("@");
            //System.out.println(isPrivate);
            //System.out.println("hjhhhghghghhghkhhgkhg");
           if(isPrivate){//私聊
               //获取目标和数据
               int idx = msg.indexOf(":");
               String targetName = msg.substring(1,idx);
               msg = msg.substring(idx+1);
               for(Channel other:all) {
                   if(other.name.equals(targetName)){//目标
                       other.send(this.name +"悄悄地对您说:"+msg);
                   }
               }
           } else{
               for(Channel other:all) {
                   if (other == this) {//自己
                    continue;
                   }
                   if (!isSys) {
                    other.send(this.name + "对所有人说:" + msg);
                   } else {
                    other.send("系统提示:" + msg);
                   }
            }

           }
        }
        //发送消息
        private void send(String msg){
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                System.out.println("-------3-------");
                release();
            }
        }
        //释放资源
        private void release(){
            this.isRunning = false;
            lijingUtils.close(dis,dos,client);
            //推出
            all.remove(this);
            sendOthers(this.name+"离开了",true);
        }

        public void run() {
            while (isRunning){
                String msg = receive();
                if(!msg.equals("")){
                    sendOthers(msg,false);
                }
            }
        }
    }
}

客户端

package com.lijing.chat05;

import java.io.*;
import java.net.Socket;
/*
在线聊天室:客户端
 */
public class Client {
    public  static void main(String[] args)throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入用户名");
        String name = br.readLine();
        System.out.println("----------Client-----------");
        //1、建立连接:使用Socket创建+服务的地址端口
        Socket client = new Socket("localhost",8888);
        //2、客户端发送消息
        new Thread(new Send(client,name)).start();
        new Thread(new Receive(client)).start();
        }
}

工具类

package com.lijing.chat05;

import java.io.Closeable;

/*
工具类
 */
public class lijingUtils {
    public  static  void  close(Closeable... targets){
        for (Closeable target:targets){
            try {
                if(null!=target)
                    target.close();
            }
            catch (Exception e){

            }
        }
    }
}

使用多线程封装的接收端

package com.lijing.chat05;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.security.PrivateKey;

/*
使用多线程封装了接收端
1、接受消息
2、释放资源
3、重写Run
 */
public class Receive implements Runnable{
    private DataInputStream dis;
    private Socket client;
    private boolean isRunning;
    public Receive(Socket client){
        this.client=client;
        try {
            dis = new DataInputStream(client.getInputStream());
            isRunning = true;
        } catch (IOException e) {
            System.out.println("====2====");
            release();
        }
    }
    //接收消息
    private  String receive(){
        String msg="";
        try {
            msg = dis.readUTF();
        } catch (IOException e) {
            System.out.println("====4=====");
            release();
        }
        return msg;
    }
    @Override
    public void run() {
        while (isRunning){
            String msg = receive();
            if(!msg.equals("")){
                System.out.println(msg);
            }

        }
    }
    //释放资源
    private void release(){
        this.isRunning = false;
        lijingUtils.close(dis,client);
    }
}

使用多线程封装的发送端

package com.lijing.chat05;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/*
使用多线程封装了发送端
1、发送消息
2、从控制台获取消息
3、释放资源
4、重写Run
 */
public class Send implements Runnable{
    private BufferedReader console;
    private DataOutputStream dos;
    private Socket client;
    private boolean isRunning;
    private String name;
    public  Send(Socket client,String name) {
        this.client=client;
        this.name =name;
        console =new BufferedReader(new InputStreamReader(System.in));
        try {
            dos = new DataOutputStream(client.getOutputStream());
            //发送名称
            send(name);
            isRunning = true;
        } catch (IOException e) {
            System.out.println("====1====");
            this.release();
        }
    }
    @Override
    public void run() {
        while (isRunning){
            String msg = getStrFromConsole();
            if(!msg.equals("")){
                send(msg);
            }
        }
    }
    //发送消息
    private  void send(String msg){
        try {
            dos.writeUTF(msg);
            dos.flush();
        } catch (IOException e) {
            System.out.println("====3====");
            release();
        }
    }
    //从控制台获取消息
    private String getStrFromConsole(){
        try {
            return console.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
    //释放资源
    private void release(){
        this.isRunning = false;
        lijingUtils.close(dos,client);
    }
}

试运行:
在这里插入图片描述

发布了15 篇原创文章 · 获赞 1 · 访问量 4141

猜你喜欢

转载自blog.csdn.net/jingli456/article/details/105013478