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的瞭解(一)


謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

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