Java Socket通信實現私聊、群聊


   前言

  閑言少敘,上代碼!

 

  代碼編寫

   server服務端

/**
 * 服務端
 */
public class Server {
    private static ServerSocket server = null;
    private static Socket ss = null;
    /**
     * 客戶端集合
     */
    private static Map<String, ServerThread> serverThreadMap = new HashMap<String, ServerThread>();


    public static void main(String[] args) {
        server();
    }

    /**
     * 普通服務器連接
     */
    private static void server() {
        try {
            //建立服務端
            server = new ServerSocket(10010);
            System.out.println("server端已啟動!");
            while (true) {
                //創建接收接口
                ss = server.accept();
                //啟動新客戶監聽線程
                new ServerThread(server, ss).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ss.close();
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 內部類線程,每連接一個新的客戶端就啟動一個對應的監聽線程
     */
    @SuppressWarnings("Duplicates")
    private static class ServerThread extends Thread {
        ServerSocket server = null;
        Socket socket = null;
        InputStream is = null;
        OutputStream os = null;
        String clientName = null;
        boolean alive = true;

        public ServerThread() {
        }

        ServerThread(ServerSocket server, Socket socket) {
            this.socket = socket;
            this.server = server;
        }

        @Override
        public void run() {
            //接收數據
            try {
                is = socket.getInputStream();
                //發送
                os = socket.getOutputStream();
                //緩存區
                byte[] b = new byte[1024];
                int length = 0;
                while (alive) {
                    //接收從客戶端發送的消息
                    length = is.read(b);
                    if (length != -1) {
                        //文本消息
                        String message = new String(b, 0, length);

                        //JSON字符串轉 HashMap
                        HashMap hashMap = new ObjectMapper().readValue(message, HashMap.class);

                        //消息類型
                        String type = (String) hashMap.get("type");

                        //新連接
                        if ("OPEN".equals(type)) {
                            clientName = (String) hashMap.get("clientName");
                            //添加客戶端到集合容器中
                            serverThreadMap.put(clientName, this);
                            System.out.println(clientName + "連接成功!");
                            System.out.println("當前客戶端數量:" + serverThreadMap.size());
                        }

                        //關閉
                        if ("CLOSE".equals(type)) {
                            alive = false;
                            System.err.println(clientName + "退出連接,關閉監聽線程!");
                        }

                        //文本消息
                        if ("MESSAGE".equals(type)) {
                            String msg = (String) hashMap.get("message");

                            String chat = (String) hashMap.get("chat");
                            //群聊(廣播)
                            if ("GROUP".equals(chat)) {
                                //遍歷容器,給容器中的每個對象轉發消息
                                for (ServerThread st : serverThreadMap.values()) {
                                    //向其他客戶端發送數據
                                    if (st != this) {
                                        st.os.write(new String(b, 0, length).getBytes());
                                    }
                                }

                                //后台打印
                                System.out.println(clientName + "向所有人說:" + msg);
                            }
                            //私聊
                            if ("PRIVATE".equals(chat)) {
                                String to = (String) hashMap.get("to");
                                serverThreadMap.get(to).os.write(new String(b, 0, length).getBytes());
                                //后台打印
                                System.out.println(clientName + "向" + to + "說:" + msg);
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.err.println("與" + clientName + "連接中斷,被迫關閉監聽線程!");
            } finally {
                try {
                    serverThreadMap.remove(clientName);
                    System.out.println("當前客戶端數量:" + serverThreadMap.size());
                    os.close();
                    is.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

  client客戶端

/**
 * 客戶端
 */
@SuppressWarnings("Duplicates")
public class Client {
    private static String serverInetAddress = "127.0.0.1";
    private static int serverPort = 10010;
    private static Socket client = null;
    private static OutputStream os = null;
    private static InputStream is = null;
    private static String thisName;
    private static boolean alive = true;

    /**
     * 客戶端連接服務器
     */
    @SuppressWarnings("unused")
    public static void open(String name) {
        try {
            thisName = name;
            InetAddress inetAddress = InetAddress.getLocalHost();
            //建立連接
            client = new Socket(serverInetAddress, serverPort);
            //數據流發送數據
            os = client.getOutputStream();
            sendMsg("{\"type\":\"OPEN\",\"clientName\":\"" + name + "\"}");
            //數據流接收數據
            is = client.getInputStream();
            byte[] b = new byte[1024];
            int length = 0;
            while (alive) {
                //接收從服務器發送回來的消息
                length = is.read(b);
                if (length != -1) {
                    onMsg(new String(b, 0, length));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //關流
                os.close();
                client.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 關閉客戶端
     */
    public static void close() {
        sendMsg("{\"type\":\"CLOSE\"}");
        alive = false;
    }

    /**
     * 發送消息
     */
    public static void sendMsg(String msg) {
        try {
            //調用發送
            os.write(msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 收到消息的回調
     */
    private static void onMsg(String message) {
        //JSON字符串轉 HashMap
        HashMap hashMap = null;
        try {
            hashMap = new ObjectMapper().readValue(message, HashMap.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

        String msg = (String) hashMap.get("message");

        String chat = (String) hashMap.get("chat");

        String from = (String) hashMap.get("from");

        String to = (String) hashMap.get("to");

        //群聊
        if ("GROUP".equals(chat)) {
            //后台打印
            System.out.println(thisName + "收到(" + to + ")群聊消息:" + msg);
        }
        //私聊
        if ("PRIVATE".equals(chat)) {
            //后台打印
            System.out.println(thisName + "收到(" + from + ")私聊消息:" + msg);
        }
    }

    /**
     * 獲取thisName
     */
    public static String getThisName() {
        return thisName;
    }
}

  controller模擬調用客戶端

    @RequestMapping("/sendMsg/{chat}/{msg}")
    public void sendMsg(@PathVariable("chat") String chat, @PathVariable("msg") String msg) {
        if ("group".equals(chat.toLowerCase())) {
            //群聊
            Client.sendMsg("{\"type\":\"MESSAGE\",\"chat\":\"GROUP\",\"from\":\""+Client.getThisName()+"\",\"to\":\"群號:xxxx\",\"message\":\"" + msg + "\"}");
        } else {
            //私聊
            Client.sendMsg("{\"type\":\"MESSAGE\",\"chat\":\"PRIVATE\",\"from\":\""+Client.getThisName()+"\",\"to\":\"" + chat + "\",\"message\":\"" + msg + "\"}");
        }
    }

    @RequestMapping("/starClient/{name}")
    public void starClient(@PathVariable("name") String name) {
        Client.open(name);
    }

    @RequestMapping("/closeClient")
    public void closeClient() {
        Client.close();
    }

 

  效果展示

  一個服務端、兩個客戶端(兩個不同的工程、模擬兩個客戶端),注意,要先啟動服務端,再啟動客戶端!

  使用controller模擬啟動兩個客戶端:

  http://localhost:10086/springboot/user/starClient/張三

  http://localhost:10087/starClient/李四

 

 

  張三發送群聊

  http://localhost:10086/springboot/user/sendMsg/group/大家好啊

 

  張三是發送者,server不再轉發此消息給張三

 

 

  張三向李四發送私聊信息

  http://localhost:10086/springboot/user/sendMsg/李四/老表,你好啊

  張三是發送者,server不再轉發此消息給張三

 

  李四回復張三私聊信息

   李四是發送者,server不再轉發此消息給李四

 

  下線、掉線

  張三:http://localhost:10086/springboot/user/closeClient

  李四:直接終止客戶端進程

 

  后記

  這個例子服務端每次有新的客戶端連接進來,就啟動一個線程去監聽與此客戶端的通信,當有大量客戶端時就不適用了,而且涉及界面時,java socket不能主動給瀏覽器發送消息,界面聊天只能用輪詢的方式實現,不好;多客戶端、涉及有界面的聊天建議使用websocket(猛戳這里 -->WebSocket+Java 私聊、群聊實例)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM