我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战

本篇内容:

后端 + 前端简单HTML页面

功能场景点:

1.  群发,所有人都能收到

2.  局部群发,部分人群都能收到

3.  单点推送, 指定某个人的页面

惯例,先看看本次实战示例项目结构:

可以看到内容不多,也就是说,springboot 整合socket, 跟着我学,轻轻松松。

古有曹植七步成诗,如今,咱们也是 7步学会整合socket!

不多说,开始:

 ① pom引入核心依赖

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <dependency>
            <groupId>com.corundumstudio.socketio</groupId>
            <artifactId>netty-socketio</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

 ② yml加上配置项

server:
  port: 8089
 
socketio:
   host: localhost
   port: 8503
   maxFramePayloadLength: 1048576
   maxHttpContentLength: 1048576
   bossCount: 1
   workCount: 100
   allowCustomRequests: true
   upgradeTimeout: 10000
   pingTimeout: 60000
   pingInterval: 25000

③ 创建socket配置加载类 MySocketConfig.java

import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 

@Configuration
public class MySocketConfig{
 
    @Value("${socketio.host}")
    private String host;
 
    @Value("${socketio.port}")
    private Integer port;
 
    @Value("${socketio.bossCount}")
    private int bossCount;
 
    @Value("${socketio.workCount}")
    private int workCount;
 
    @Value("${socketio.allowCustomRequests}")
    private boolean allowCustomRequests;
 
    @Value("${socketio.upgradeTimeout}")
    private int upgradeTimeout;
 
    @Value("${socketio.pingTimeout}")
    private int pingTimeout;
 
    @Value("${socketio.pingInterval}")
    private int pingInterval;
 
    @Bean
    public SocketIOServer socketIOServer() {
        SocketConfig socketConfig = new SocketConfig();
        socketConfig.setTcpNoDelay(true);
        socketConfig.setSoLinger(0);
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        buildSocketConfig(socketConfig, config);
        return new SocketIOServer(config);
    }
 
    
    @Bean
    public SpringAnnotationScanner springAnnotationScanner() {
        return new SpringAnnotationScanner(socketIOServer());
    }
 
    private void buildSocketConfig(SocketConfig socketConfig, com.corundumstudio.socketio.Configuration config) {
        config.setSocketConfig(socketConfig);
        config.setHostname(host);
        config.setPort(port);
        config.setBossThreads(bossCount);
        config.setWorkerThreads(workCount);
        config.setAllowCustomRequests(allowCustomRequests);
        config.setUpgradeTimeout(upgradeTimeout);
        config.setPingTimeout(pingTimeout);
        config.setPingInterval(pingInterval);
    }
}

④创建消息实体 MyMessage.java


public class MyMessage {
 
    private String type;
 
    private String content;
 
    private String from;
 
    private String to;
 
    private String channel;
 
 
    public String getType() {
        return type;
    }
 
    public void setType(String type) {
        this.type = type;
    }
 
    public String getContent() {
        return content;
    }
 
    public void setContent(String content) {
        this.content = content;
    }
 
    public String getFrom() {
        return from;
    }
 
    public void setFrom(String from) {
        this.from = from;
    }
 
    public String getTo() {
        return to;
    }
 
    public void setTo(String to) {
        this.to = to;
    }
 
    public String getChannel() {
        return channel;
    }
 
    public void setChannel(String channel) {
        this.channel = channel;
    }
}

代码简析:

⑤创建 socket handler 负责记录客户端 连接、下线

MySocketHandler.java

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.socket.mysocket.util.SocketUtil;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
 

@Component
public class MySocketHandler {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private SocketIOServer socketIoServer;
    @PostConstruct
    private void start(){
        try {
            socketIoServer.start();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @PreDestroy
    private void destroy(){
        try {
        socketIoServer.stop();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    @OnConnect
    public void connect(SocketIOClient client) {
        String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
        SocketUtil.connectMap.put(userFlag, client);
        log.info("客户端userFlag: "+ userFlag+ "已连接");
    }
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
        log.info("客户端userFlag:" + userFlag + "断开连接");
        SocketUtil.connectMap.remove(userFlag, client);
    }
}

代码简析:

 ⑥ 封装的socket 小函数

SocketUtil.java

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
 
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
 

@Component
public class SocketUtil {
 
    private final Logger log = LoggerFactory.getLogger(this.getClass());
 
    //暂且把用户&客户端信息存在缓存
    public static ConcurrentMap<String, SocketIOClient> connectMap = new ConcurrentHashMap<>();
 
    @OnEvent(value = "CHANNEL_SYSTEM")
    public void systemDataListener(String receiveMsg) {
        if (!StringUtils.hasLength(receiveMsg)){
            return;
        }
        JSONObject msgObject = (JSONObject) JSON.parse(receiveMsg);
        String userFlag = String.valueOf(msgObject.get("from"));
        String content = String.valueOf(msgObject.get("content"));
        log.info("收到用户 : {} 推送到系统频道的一条消息 :{}",userFlag,content );
    }
 
    public void sendToAll(Map<String, Object> msg,String sendChannel) {
        if (connectMap.isEmpty()){
            return;
        }
        //给在这个频道的每个客户端发消息
        for (Map.Entry<String, SocketIOClient> entry : connectMap.entrySet()) {
            entry.getValue().sendEvent(sendChannel, msg);
        }
    }
 
    public void sendToOne(String userFlag, Map<String, Object> msg,String sendChannel) {
        //拿出某个客户端信息
        SocketIOClient socketClient = getSocketClient(userFlag);
        if (Objects.nonNull(socketClient) ){
            //单独给他发消息
            socketClient.sendEvent(sendChannel,msg);
        }
    }
 
 
    
    public SocketIOClient getSocketClient(String userFlag){
        SocketIOClient client = null;
        if (StringUtils.hasLength(userFlag) &&  !connectMap.isEmpty()){
            for (String key : connectMap.keySet()) {
                if (userFlag.equals(key)){
                    client = connectMap.get(key);
                }
            }
        }
        return client;
    }
 
 
 
}

代码简析:

⑦写1个接口,模拟场景,前端页面调用后端接口,做消息推送

TestController.java

import com.socket.mysocket.dto.MyMessage;
import com.socket.mysocket.util.SocketUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
 
import java.util.HashMap;
import java.util.Map;
 

@RestController
public class TestController {
    public final static String SEND_TYPE_ALL = "ALL";
    public final static String SEND_TYPE_ALONE = "ALONE";
    @Autowired
    SocketUtil socketUtil;
 
    @PostMapping("/testSendMsg")
    public String testSendMsg(@RequestBody MyMessage myMessage){
        Map<String, Object> map = new HashMap<>();
        map.put("msg",myMessage.getContent());
 
        //群发
        if (SEND_TYPE_ALL.equals(myMessage.getType())){
            socketUtil.sendToAll( map,myMessage.getChannel());
            return "success";
        }
        //指定单人
        if (SEND_TYPE_ALONE.equals(myMessage.getType())){
            socketUtil.sendToOne(myMessage.getTo(), map, myMessage.getChannel());
            return "success";
        }
 
        return "fail";
    }
}

代码简析:

好了,7步了。一切已经就绪了。

前端简单页面

接下来搞点前端HTML页面, 玩一玩看看效果:

第一个页面:

TestClientStudentJC.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>我要连SOCKET</title>
    <base>
    <script class="lazy" data-src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
    <script class="lazy" data-src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
    <style>
        body {
            padding: 20px;
        }
        #console {
            height: 450px;
            overflow: auto;
        }
        .msg-color {
            color: green;
        }
    </style>
</head>
 
<body>
<div id="console" class="well"></div>
 
 
<div id="conversationDiv">
    <labal>给系统推消息</labal>
    <input type="text" id="content"/>
    <button id="btnSendToSystem" onclick="sendSys();">发送</button>
</div>
 
 
</body>
<script type="text/javascript">
    var socket;
    connect();
 
    function connect() {
        var userFlag = 'user_JC';
        var opts = {
            query: 'userFlag=' + userFlag
        };
        socket = io.connect('http://localhost:8503', opts);
        socket.on('connect', function () {
            console.log("连接成功");
            output('当前用户是:' + userFlag );
            output('<span class="msg-color">连接成功了。</span>');
        });
        socket.on('disconnect', function () {
            output('<span class="msg-color">下线了。 </span>');
        });
 
        socket.on('CHANNEL_STUDENT', function (data) {
            let msg= JSON.stringify(data)
            output('收到学生频道消息了:' + msg );
            console.log(data);
 
        });
        socket.on('CHANNEL_SYSTEM', function (data) {
            let msg= JSON.stringify(data)
            output('收到系统全局消息了:' + msg );
            console.log(data);
 
        });
 
    }
 
    function sendSys() {
        console.log('发送消息给服务端');
        var content = document.getElementById('content').value;
 
        socket.emit('CHANNEL_SYSTEM',JSON.stringify({
            'content': content,
            'from': 'user_JC'
        }));
 
    }
    function output(message) {
        var element = $("<div>" + message + "</div>");
        $('#console').prepend(element);
    }
    
</script>
</html>

代码简析:

第二个页面,跟第一个基本一样,改一下用户唯一标识:

TestClientStudentPU.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>我要连SOCKET</title>
    <base>
    <script class="lazy" data-src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
    <script class="lazy" data-src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
    <style>
        body {
            padding: 20px;
        }
        #console {
            height: 450px;
            overflow: auto;
        }
        .msg-color {
            color: green;
        }
    </style>
</head>
 
<body>
<div id="console" class="well"></div>
 
 
<div id="conversationDiv">
    <labal>给系统推消息</labal>
    <input type="text" id="content"/>
    <button id="btnSendToSystem" onclick="sendSys();">发送</button>
</div>
 
 
</body>
<script type="text/javascript">
    var socket;
    connect();
 
    function connect() {
        var userFlag = 'user_PU';
        var opts = {
            query: 'userFlag=' + userFlag
        };
        socket = io.connect('http://localhost:8503', opts);
        socket.on('connect', function () {
            console.log("连接成功");
            output('当前用户是:' + userFlag );
            output('<span class="msg-color">连接成功了。</span>');
        });
        socket.on('disconnect', function () {
            output('<span class="msg-color">下线了。 </span>');
        });
 
        socket.on('CHANNEL_STUDENT', function (data) {
            let msg= JSON.stringify(data)
            output('收到学生频道消息了:' + msg );
            console.log(data);
 
        });
        socket.on('CHANNEL_SYSTEM', function (data) {
            let msg= JSON.stringify(data)
            output('收到系统全局消息了:' + msg );
            console.log(data);
 
        });
 
    }
 
    function sendSys() {
        console.log('发送消息给服务端');
        var content = document.getElementById('content').value;
 
        socket.emit('CHANNEL_SYSTEM',JSON.stringify({
            'content': content,
            'from': 'user_PU'
        }));
 
    }
    function output(message) {
        var element = $("<div>" + message + "</div>");
        $('#console').prepend(element);
    }
 
</script>
</html>

OK,把项目跑起来,开始玩。

直接访问客户端页面 模拟学生 JC连接socket:
http://127.0.0.1:8089/TestClientStudentJC.html

 可以看到服务端有监测到:

 这里监测的:

先试试客户端给系统推消息先:

 可以看到服务端成功收到消息:

 这种方式,其实是因为服务监听了相关的频道:

 前端使用JS推到这个系统频道:

ps: 其实前端给服务端推消息,其实调用接口就可以。

OK,进入核心应用场景1:

 群发,所有人都能收到

 系统给连上的客户端都推送消息

{
"type": "ALL",

"content":"你们好,这是一条广播消息,全部人都能收到",

"channel":"CHANNEL_SYSTEM"

}

看看效果

场景2,局部群发,部分人群都能收到

其实也就是通过HTML 客户端监听主题做区分就好:

直接拉人口,升3 :

模拟2个学生,1个老师都连接上了socket

当然,老师监听的是 老师频道:

然后我们模拟推送一下消息到指定的老师频道: 

{
"type": "ALL",

"content":"给老师们推一条消息!!!",

"channel":"CHANNEL_TEACHER"

}

最后一个场景,也就是单点推送,指定某个人收到

模拟 学生 PU 给 学生JC 推消息:

可以看到在学生频道的JC正常收到了PU的消息:

到此这篇关于Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战的文章就介绍到这了,更多相关Springboot Socket实战内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战

本文主要介绍了Springboot整合Socket实现单点发送,广播群发,1对1,1对多实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2022-11-13

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录