1. 程式人生 > >Java Socket通訊實現私聊、群聊 WebSocket+Java 私聊、群聊例項

Java Socket通訊實現私聊、群聊 WebSocket+Java 私聊、群聊例項

   前言

  閒言少敘,上程式碼!

 

  程式碼編寫

   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 私聊、群聊例項)。