socket.io 使用protobuf 协议发送消息

news2024/11/28 18:57:29

一、服务端

     1、maven引入netty-socketio

<dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.22</version>
        </dependency>

  2、服务端java代码

@Service
public class SocketService {

    @Autowired
    private SocketConfig socketConfig;

    private SocketIOServer socketIOServer = null;

    @Autowired(required = false)
    private ISocketLinesService socketLinesService;


    //pc端
    private Map<String /*userId*/, Set<UUID>> CLIENT_ID = new HashMap<>();
    //移动端
    private Set<String> MOBILE_ID = new HashSet<>();
    private Set<String> PC_COUNT = new HashSet<>();

    @PostConstruct
    public void init() {

        Configuration config = this.socketConfig.getConfig();
        if (config == null) {
            // 配置默认的启动信息
            config = new Configuration();
            config.setHostname("0.0.0.0");
            config.setPort(7543);
        }
        int num = Runtime.getRuntime().availableProcessors();

        //主线程设置为1即可
        config.setBossThreads(1);
        //设置工作线程
        config.setWorkerThreads(num * 2);

        //解决对此重启服务时,netty端口被占用问题
        com.corundumstudio.socketio.SocketConfig tmpConfig = new com.corundumstudio.socketio.SocketConfig();
        tmpConfig.setReuseAddress(true);
        config.setSocketConfig(tmpConfig);


        //异常处理
        config.setExceptionListener(new ExceptionListener() {

            @Override
            public void onPongException(Exception e, SocketIOClient client) {

            }

            @Override
            public void onEventException(Exception e, List<Object> args, SocketIOClient client) {
//                System.out.println("收发消息异常");
            }

            @Override
            public void onDisconnectException(Exception e, SocketIOClient client) {
//                System.out.println("断开连接异常");
            }

            @Override
            public void onConnectException(Exception e, SocketIOClient client) {
//                System.out.println("建立连接异常");
            }

            @Override
            public void onPingException(Exception e, SocketIOClient client) {
//                System.out.println("心跳异常");
            }

            @Override
            public boolean exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
                return false;
            }
        });
        this.socketIOServer = new SocketIOServer(config);
        this.addEvent();
        System.out.println(String.format("socket服务已启动,地址:%s,端口:%d", config.getHostname(), config.getPort()));

        this.socketIOServer.start();

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("应用程序关闭了");
            socketIOServer.stop();
        }));
    }

    @PreDestroy
    public void stop() {
        if (this.socketIOServer != null) {
            this.socketIOServer.stop();
        }
    }

    public void addEvent() {
        this.socketIOServer.addEventListener("emit", String.class, new DataListener<String>() {
            @Override
            public void onData(SocketIOClient client, String data, AckRequest ackRequest) {
                System.out.println(String.format("收到的消息:%s", data));
            }
        });

        this.socketIOServer.addConnectListener(new ConnectListener() {
            @Override
            public void onConnect(SocketIOClient client) {
                //处理会话建立业务
                Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
                if (params == null || params.size() == 0) {
                    client.disconnect();
                    return;
                }
                try {
                    line(params, 1, client);
                } catch (RuntimeException e) {
                    //校验身份失败
                    client.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        this.socketIOServer.addDisconnectListener(new DisconnectListener() {
            @Override
            public void onDisconnect(SocketIOClient client) {
//                System.out.println("断开连接了");
                // 处理会话断开业务
                Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
                try {
                    line(params, 2, client);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void line(Map<String, List<String>> params, int type, SocketIOClient client) {
        List<String> list = params.get("userId");
        if (list == null || list.size() == 0) {
            send("身份认证失效");
            client.disconnect();
            return;
        }
        String user_id = params.get("userId").get(0);
        Set<UUID> uids = CLIENT_ID.get(user_id);
        if (uids == null) {
            uids = new HashSet<>();
        }
        System.out.println("clientId:" + user_id);
        uids.add(client.getSessionId());
        if (type == 1) {
            if (user_id.equalsIgnoreCase("undefined") || user_id.equals("")) {
                client.disconnect();
            } else {
                CLIENT_ID.put(user_id, uids);
                PC_COUNT.add(user_id);
            }
        } else {
            uids.remove(client.getSessionId());
            PC_COUNT.remove(user_id);
        }

        //移动端在线
        handleMobileLine(user_id, params, type, client);

    }

    public void handleMobileLine(String userId, Map<String, List<String>> params, int type, SocketIOClient client) {
        if (StringUtils.isEmpty(userId) || userId.equalsIgnoreCase("undefined")) {
            return;
        }
        List<String> list = params.get("mobile");
        if (list != null && list.size() > 0) {
            UUID id = client.getSessionId();
            if (type == 1) {
                MOBILE_ID.add(userId);
            } else {
                MOBILE_ID.remove(userId);
            }

        }
    }

    public void sendAll(String topic, byte[] data) {
        this.socketIOServer.getBroadcastOperations().sendEvent(topic, data);
    }

    public void sendOne(String topic, String clientId, byte[] data) {
        Set<UUID> uids = CLIENT_ID.get(clientId);
        if (uids != null) {
            for (UUID uid : uids) {
                this.socketIOServer.getClient(uid).sendEvent(topic, data);
            }
        }
    }

    public void send(String data) {
        this.socketIOServer.getBroadcastOperations().sendEvent("message", data);
    }

    public void send(String event, String data) {
        this.socketIOServer.getBroadcastOperations().sendEvent(event, data);
    }

    public void sendOne(String event, String data, String userId) {
        Set<UUID> uids = CLIENT_ID.get(userId);
        if (uids != null) {
            for (UUID uid : uids) {
                this.socketIOServer.getClient(uid).sendEvent(event, data);
            }
        }
    }

    /**
     * 刷新在线人数
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void reshCount() {
        if (this.socketLinesService != null) {
            this.socketLinesService.reshCount(0, PC_COUNT.size());
            this.socketLinesService.reshCount(1, MOBILE_ID.size());
        }
    }

}

3、 消息发送工具

@Component
public class SendMsgUtil {

    private static SendMsgUtil sendMsgUtil;

    @Autowired
    private SocketService socketService;

    @PostConstruct
    public void start() {
        sendMsgUtil = this;
    }

    public static void send2User(String userId, String event, MsgEntity msg) {
        sendMsgUtil.socketService.sendOne(event, JSON.toJSONString(msg), userId);
    }

    public static void send2User(String userId, String event, byte[] data) {
        sendMsgUtil.socketService.sendOne(event, userId, data);
    }

    public static void send2User(String userId, MsgEntity msg) {
        try {
            sendMsgUtil.socketService.sendOne("message", JSON.toJSONString(msg), userId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void sendAll(String event, String data) {
        sendMsgUtil.socketService.send(event, data);
    }


    public static void sendAll(String event, byte[] data) {
        sendMsgUtil.socketService.sendAll(event, data);
    }


}

4、发送消息

@RestController
@RequestMapping(value = "admin/demo")
public class TestSocketController {


    @ApiOperation(value = "测试发送")
    @GetMapping(value = "send")
    public ResultMsg send(
            @RequestParam String name
    ) {

        //
        PlayerOuterClass.Player.Builder builder = PlayerOuterClass.Player.newBuilder();
        builder.setId(1).setName(name).setEnterTime(System.currentTimeMillis());

        PlayerOuterClass.Player player = builder.build();
        System.out.println(player.getName());

        //发送普通文本消息
        SendMsgUtil.sendAll("topic", "cn_yaojin");
        
        //发送protobuf协议消息
        SendMsgUtil.sendAll("topic1", player.toByteArray());
        return ResultMsg.builder();
    }


}

5、proto文件:Player.proto,使用idea插件生成java代码

syntax = "proto3";
package com.cn.web.msg;

message Player {
    uint32  id = 1;         //唯一ID  首次登录时设置为0,由服务器分配
    string  name = 2;       //显示名字
    uint64  enterTime = 3;  //登录时间
}

  

 

二、前端

<!DOCTYPE html>
<html>

<head>
    <title>Socket.IO chat</title>
    <style>
        body {
            margin: 0;
            padding-bottom: 3rem;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        }

        #form {
            background: rgba(0, 0, 0, 0.15);
            padding: 0.25rem;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            display: flex;
            height: 3rem;
            box-sizing: border-box;
            backdrop-filter: blur(10px);
        }

        #input {
            border: none;
            padding: 0 1rem;
            flex-grow: 1;
            border-radius: 2rem;
            margin: 0.25rem;
        }

        #input:focus {
            outline: none;
        }

        #form>button {
            background: #333;
            border: none;
            padding: 0 1rem;
            margin: 0.25rem;
            border-radius: 3px;
            outline: none;
            color: #fff;
        }

        #messages {
            list-style-type: none;
            margin: 0;
            padding: 0;
        }

        #messages>li {
            padding: 0.5rem 1rem;
        }

        #messages>li:nth-child(odd) {
            background: #efefef;
        }
    </style>

    <!-- 引入2.0版本的socket.io文件 -->
    <script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js" ></script>
    <!-- 引入protobuf文件 -->
    <script src="//cdn.jsdelivr.net/npm/protobufjs@7.X.X/dist/protobuf.js"></script>
</head>

<body>
    <ul id="messages"></ul>
    <form id="form" action="">
        <input id="input" autocomplete="off" /><button>Send</button>
    </form>


    <script type="module">
      
        //建立socket连接
        const socket = io("http://127.0.0.1:3091", {
            reconnectionDelayMax: 10000,
            auth: {
                token: "123"
            },
            query: {
                "userId": "cn_yaojin"
            }
        });


        socket.emit("emit","hello world !");

        socket.on("connect", () => {
            console.log("通道已建立")
        });

        //topic类型消息是服务端发送的普通文本
        socket.on("topic", (data) => {
            console.log("收到新的数据了");
        });

        //加载Player.proto文件(在前端代码目录下的某个位置,我放在了当前目录) 并解析收到socket数据(服务端发送的:topic1 类型消息
        protobuf.load("Player.proto", function (err, root) {
            
            // Player 是proto文件中定义的实体名称
            const tickerData = root.lookupType("Player");

            //监听服务端发送的 topic1 消息
            socket.on("topic1", (data) => {
                console.log("收到新的数据了");

                //解析数据
                var message = tickerData.decode(new Uint8Array(data));
                var object = tickerData.toObject(message, {
                    longs: String,
                    enums: String,
                    bytes: String,
                    // see ConversionOptions
                });

                console.log(object)
                
                   
                //另一种解析方式
                // var d = new Uint8Array(data.byteLength);
                // var dataView = new DataView(data);
                // for (var i = 0; i < data.byteLength; i++) {
                //     d[i] = dataView.getInt8(i);
                // }
                // //解析后的数据
                // let decoded = tickerData.decode(d);
                // console.log(`decoded = ${JSON.stringify(decoded)}`);
                // console.log(decoded.id+"---"+decoded.name)

            });
            

        });

       
    </script>
</body>

</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/88136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

艾美捷内皮细胞生长添加剂不同研究工具推荐

艾美捷Relia Tech内皮细胞生长添加剂可用于培养没有饲养细胞的杂交瘤细胞&#xff08;Pintus&#xff0c;1983&#xff09;。这些脑提取物中的活性因子已被鉴定为aFGF的变体&#xff08;Burgesse&#xff0c;1985&#xff09;。该ECGF产品补充了生理浓度的人rec.VEGF-A&#xf…

初识 Markdown编辑器

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

真兰仪表通过深交所注册:拟募资17.4亿 上半年净利下降27%

雷递网 雷建平 12月13日上海真兰仪表科技股份有限公司&#xff08;简称&#xff1a;“真兰仪表”&#xff09;日前通过注册&#xff0c;准备在深交所创业板上市。真兰仪表计划募资17.4亿元。其中&#xff0c;7.76亿用于真兰仪表科技有限公司燃气表产能扩建项目&#xff0c;6.12…

NFA的确定化

一、实验目的 &#xff08;1&#xff09;通过本次实验&#xff0c;加深对正则表达式、NFA、DFA及其识别的语言的理解&#xff1b; &#xff08;2&#xff09;掌握从NFA到DFA的转换&#xff0c;以及用子集法把NFA转换成DFA理论&#xff0c;编程实现将NFA&#xff08;不确定有穷…

SAP 事务代码BD20不能处理状态为51的IDoc

SAP 事务代码BD20不能处理状态为51的IDoc 对于SAP IDoc相关的事务代码比如WE02,WE19,BD87等都比较熟悉&#xff0c;因为使用的比较多。但是对于事务代码BD20却很少使用。 笔者在近期的一个项目上&#xff0c;听到客户的global team有使用该事务代码&#xff0c;设置成了一个job…

Oracle RMAN备份相关信息查询

查询 RMAN 备份状态主要是通过视图V$RMAN_STATUS 来进行&#xff0c;这个视图可以查询到 RMAN 执行的操作。 我们主要查看两列&#xff1a;OPERATION 和 STATUS。 OPERATION 的值有&#xff1a;RMAN、BACKUP、DELETE、CROSSCHECK、DELETE OBSOLETE等。 STATUS的值有&#xff1a…

中国电信携手华为建成全球首个支持5G RedCap联合测试能力的5G开放实验室

近日&#xff0c;中国电信物联网开放实验室宣布与华为共同完成了5G RedCap实验室技术验证&#xff0c;并建成了全球首个具备5G R17标准RedCap联合测试能力的开放实验室。 此次技术验证&#xff0c;为RedCap的规模商用奠定了坚实基础&#xff0c;有助于打造RedCap行业终端生态认…

良心总结!Git 各指令的本质,真是通俗易懂啊

1前言 作为当前世界上最强大的代码管理工具Git相信大家都很熟悉&#xff0c;但据我所知有很大一批人停留在clone、commit、pull、push...的阶段&#xff0c;是不是对rebase心里没底只敢用merge&#xff1f;碰见版本回退就抓瞎&#xff1f;别问我怎么知道的&#xff0c;问就是&…

什么是成熟的自动化运维平台?

本文首发于知乎&#xff0c;由嘉为蓝鲸原创。 商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 当企业遇到运维管理对象的急速增长&#xff0c;业务需求频繁变更等传统运维场景问题时&#xff0c;依靠手工运维已经远远满足不了需求&#xff0c;因此我们需要搭建…

2022,itbird的年终总结报告

最近公司要求个人在做年终总结了&#xff0c;趁着这个机会&#xff0c;也想对自己的2022年进行一下回顾总结&#xff0c;最重要的是&#xff0c;对2023的目标&#xff0c;可以有一个指引。 就从工作和生活两方面来讲吧。 1.工作 1.1 行业的状态 本人从事的是android开发工作…

Cuda个别库函数的兼容性 - shuffle\数学库\原子

兼容性针对的是不同的Cuda版本和设备计算能力(compute capability) shuffle 在C\C扩展一节 新版本函数见Cuda12.0 文档 __shfl_sync, __shfl_up_sync, __shfl_down_sync, and __shfl_xor_sync exchange a variable between threads within a warp. Supported by devices of …

信创入围认证详解

信创是一个统称概念&#xff0c;实际是把现有与信息技术相关的行业结合在一起&#xff0c;命名为“信息技术应用创新产业”&#xff0c;简称“信创”。一般来说&#xff0c;信创包括基础硬件、基础软件、应用软件、信息安全四大板块。其中&#xff0c;基础硬件主要包括&#xf…

让最近爆火的ChatGPT来谈谈,作为一个技术人该如何写好一篇技术博文

ChatGPT 是由 OpenAI 训练的一个大型语言模型。专门设计用于回答用户提出的问题&#xff0c;我可以提供有价值的信息&#xff0c;并帮助用户解决问题 下面的回答均来自ChatGPT CharGPT如何写好一篇技术博文&#xff1f;写技术博文需要具备那些能力就用java实现冒泡排序来写一篇…

C语言画贝塞尔曲线的函数

程序截图 简单说明 这个函数就是 void drawBezierCurve(COLORREF color, const unsigned int len, ...) color 是贝塞尔曲线的颜色&#xff0c;len 是画出贝塞尔曲线所需要的点的个数&#xff0c;最少 1 个&#xff0c;不要乱传。之后的参数传的就是画出贝塞尔曲线要的点&am…

大数据时代,数据实时同步解决方案的思考—最全的数据同步总结

F、 客户端开发&#xff0c;在maven中引入canal的依赖 com.alibaba.otter canal.client 1.0.21 代码示例&#xff1a; package com.example; import com.alibaba.otter.canal.client.CanalConnector; import com.alibaba.otter.canal.client.CanalConnectors; import c…

[附源码]Nodejs计算机毕业设计基于Web美食网站设计Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

C++ Reference: Standard C++ Library reference: Containers: map: map: begin

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/begin/ 公有成员函数 <map> std::map::begin C98 iterator begin(); const_iterator begin() const; C11 iterator begin() noexcept; const_iterator begin() const noexcept;返回指向开始的iterato…

[附源码]Python计算机毕业设计甘肃草地植物数字化标本库管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

图拉普拉斯矩阵

正定矩阵 在线性代数里&#xff0c;正定矩阵 (positive definite matrix) 有时会简称为正定阵。 广义定义&#xff1a;设M是n阶方阵&#xff0c;如果对任何非零向量z&#xff0c;都有z⃗TMz⃗>0\vec{z}^TM\vec{z}>0zTMz>0&#xff0c;则称M为正定矩阵。 狭义定义&…

CSS之display:grid的用法和动态:before content内容

CSS之display:grid的用法和动态:before content内容1. display:grid的用法2.动态:before content内容3.完整代码&#xff1a;项目诉求&#xff1a; 突然有个需求&#xff0c;就是 指定行列&#xff0c;并呈现N字型展示数据&#xff0c;如下所示&#xff1a; 有纠结是用display:…