array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Java Socket通信实现私聊、群聊 - 爱码网
Im-Victor

 前言

  闲言少叙,上代码!

 

  代码编写

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

分类:

技术点:

相关文章: