TCP实现聊天室(五)

勿以恶小而为之,勿以善小而不为--------------------------刘备
劝诸君,多行善事积福报,莫作恶

上一章简单介绍了TCP通信(四),如果没有看过,请观看上一章

通过TCP 通信,实现简单的聊天室功能,包括群发消息和私发消息。 只发送普通的字符串信息, 如果想上传文件等,可以按照上一章节的内容,进行改写。

私发消息,有一个固定的格式, @私发的人:私发的消息

与上一章节中的 多线程 echo 部分内容差不多。

一. TCP 实现聊天室功能

一.一 关闭工具类 CloseUtils

public class CloseUtils {

    /**
     * 关闭
     * @param closeables
     */
    public static void close(Closeable...closeables){

        for(Closeable closeable:closeables){

            if(null!=closeable){
                try {
                    closeable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

一.二 多线程发送 Send

//发送者
public class Send implements  Runnable{

    private Socket client;
    private DataOutputStream dataOutputStream;

    private BufferedReader bufferedReader;

    private boolean isRunning;

    private String nickName;

    /**
     * 接收过来暱称
     * @param client
     * @param nickName
     */
    public Send(Socket client,String nickName){

        this.client=client;

        this.isRunning=true;

        this.nickName=nickName;
        try {
            this.dataOutputStream=new DataOutputStream(client.getOutputStream());

            this.bufferedReader=new BufferedReader(new InputStreamReader(System.in));

            //把暱称发送过去, 提示谁谁谁 进来了
            sendMsg(nickName);

        } catch (IOException e) {
            e.printStackTrace();
            isRunning=false;
        }
    }
    @Override
    public void run() {
        while(this.isRunning){
            try {
                //接收数据
                String content=bufferedReader.readLine();

                if(content!=null&&!"".equals(content.trim())){
                    //发送数据
                    sendMsg(content);

                    if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){

                        stop();
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public void stop(){

        this.isRunning=false;
    }

    public void sendMsg(String msg){
        try {
            dataOutputStream.writeUTF(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

一.三 多线程接收 Receiver

//接收者
public class Receiver implements  Runnable{

    private Socket client;

    private DataInputStream dataInputStream;

    private boolean isRunning;

    private String nickName;

    /**
     * 接收消息
     * @param client
     * @param nickName
     */
    public Receiver(Socket client,String nickName){

        this.client=client;
        this.isRunning=true;
        this.nickName=nickName;
        try {
            this.dataInputStream=new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
            this.isRunning=false;
            // CloseUtils.close(client);
        }
    }

    @Override
    public void run() {
        while(this.isRunning){

            String content=readMsg();

            System.out.println(content);

            if("欢迎下次再来".equalsIgnoreCase(content)){
                stop();
            }
        }
        try {
            CloseUtils.close(this.dataInputStream,client.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    public void stop(){

        this.isRunning=false;
    }

    public String readMsg(){

        String content="";
        try {
            content= dataInputStream.readUTF();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }
}

一.四 客户端 Client

public class Client {
    public static void main(String[] args) {
        System.out.println("-------------正在连接服务器-----------");

        try {
            Socket socket=new Socket("localhost",9999);

            BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));

            System.out.println("请输入你的暱称:");

            String nickName=bufferedReader.readLine();

            //将暱称传入
            new Thread(new Send(socket,nickName)).start();

            new Thread(new Receiver(socket,nickName)).start();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

一.五 服务器端 Server

public class Server {

    //客户多线程 
    static class Channel implements  Runnable{

        private Socket client;

        private DataInputStream dataInputStream;

        private DataOutputStream dataOutputStream;

        private boolean isRunning;

        private String nickName;

        public Channel(Socket client){

            this.client=client;
            this.isRunning=true;

            try {
                this.dataInputStream=new DataInputStream(client.getInputStream());
                this.dataOutputStream=new DataOutputStream(client.getOutputStream());

                //暱称是读取的
                this.nickName=readMsg();

                //自己控制台展示的
                sendMsg("欢迎您的到来");

                //发送给大家的,群发的系统消息
                sendOtherMsg("大家欢迎"+nickName+"来到聊天室",true);



            } catch (IOException e) {
                e.printStackTrace();

                CloseUtils.close(dataInputStream,dataOutputStream);
            }

        }

        @Override
        public void run() {

            while(this.isRunning){

                String content=readMsg();


                //对接收的消息进行相应的判断处理。

                String responseData="";

                if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){
                    sendMsg("欢迎下次再来");
                    sendOtherMsg(nickName+"离开了聊天室",true);
                    this.isRunning=false;

                    //将当前对象移除
                    clientList.remove(this);

                }else{
                    responseData=nickName+"对大家说:"+content;

                    //私发是 @私发的人:  消息
                    if(content.startsWith("@")){
                        sendOne(content);
                    }else{
                        sendOtherMsg(responseData,false);
                    }
                }

            }

        }

        /**
         * 发送给单独的一个人
         * @param msg
         */
        public void sendOne(String msg){
                int index=msg.indexOf(":");

                if(index<0){
                    return ;
                }
                //发送给谁
                String toName=msg.substring(1,index);

                //发送的内容
                String content=msg.substring(index+1);


                for(Channel channel:clientList){

                    //找到了这个人
                    if(toName.equalsIgnoreCase(channel.nickName)){

                        channel.sendMsg(nickName+"悄悄地对你说:"+content);
                        break;
                    }
                }
        }

        //发送给其他人,不包括他自己。
        public void sendOtherMsg(String msg,boolean isSysInfo){
            //看是否是系统消息
            if(isSysInfo){
                //是系统消息
                for(Channel channel:clientList){
                    //是当前对象
                    if(channel==this){
                        continue;
                    }
                    channel.sendMsg(msg);
                }
            }else{
                //普通群发消息
                for(Channel channel:clientList){
                    channel.sendMsg(msg);
                }

            }
        }

        public void sendMsg(String msg){
            try {
                dataOutputStream.writeUTF(msg);
            } catch (IOException e) {
                // e.printStackTrace();
                CloseUtils.close(dataInputStream,dataOutputStream);
            }
        }
        public String readMsg(){

            String content="";
            try {
                content= dataInputStream.readUTF();
            } catch (IOException e) {
                CloseUtils.close(dataInputStream,dataOutputStream);
                //e.printStackTrace();
            }
            return content;
        }
    }
    //定义集合,在高并发时用 CopyOnWriteArrayList, 与以前的ArrayList 非并发 差不多

    private static CopyOnWriteArrayList<Channel> clientList=new CopyOnWriteArrayList<Channel>();

    public static void main(String[] args) {

        try {
            ServerSocket serverSocket=new ServerSocket(9999);

            System.out.println("********服务器开启************");

            while(true){
                Socket client=serverSocket.accept();//多线程运行

                Channel channel=new Channel(client);
                //添加到集合里面
                clientList.add(channel);

                //启动线程
                new Thread(channel).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

一.六 运行测试

重启服务器,依次运行三个客户端,分别输入暱称为 a,b,c

那么此时, a客户端控制台打印输出

有图片

b客户端打印输出

有图片

c客户端打印输出

有图片

不会接收到加入之前的数据。

此时,a 输入一句话,

有图片

那么 b,c 都能够收到这句话。

在这里插入图片描述在这里插入图片描述
b,c 也可以回应

有图片

可以进行私发, 如 私发给 c

有图片

那么这个时候, b应该看不到这个消息, c可以看到

b控制台

有图片

c控制台
在这里插入图片描述

如果c 客户 输入 bye, 就可以退出聊天室了

c控制台

有图片

那么这个时候 a就可以接收到系统提示的 c离开聊天室的消息了

有图片

这样,就实现了简单的聊天室的群发和私发功能 。

其实,关于聊天室,可以使用 WebSocket 来实现。

可以看老蝴蝶写的 WebSocket 内容。 WebSocket的了解(一)


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章