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

posted @ 2018-12-03 17:57  huanzi-qch  阅读(7174)  评论(0编辑  收藏  举报