java中springboot项目集成socketIo实现实时推送

今天在这里跟大家分享一下springboot项目集成socketIo实现实时推送功能。不多说什么直接上代码,然后慢慢讲解。

第一步项目中准备socketIo的运行环境

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

第二步 socketIo的运行类,和启动类。

@Configuration
public class NettySocketConfig {
    private String ipWin;
    private String ipLinxu;
    private int port;

    @Bean
    public SocketIOServer socketIOServer() throws Exception{
        ipWin = (String)YmlUtil.getValue("socketIo.win");
        ipLinxu = (String)YmlUtil.getValue("socketIo.linxu");
        port = (Integer) YmlUtil.getValue("socketIo.port");

        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        String os = System.getProperty("os.name");
        if(os.toLowerCase().startsWith("win")){
            //在本地window环境测试时用localhost
            config.setHostname(ipWin);
        } else {
            //部署到你的远程服务器正式发布环境时用服务器公网ip
            config.setHostname(ipLinxu);
        }
        // 端口,任意
        config.setPort(port);
        config.setMaxFramePayloadLength(1024 * 1024);
        config.setMaxHttpContentLength(1024 * 1024);
        //该处进行身份验证h
        config.setAuthorizationListener(handshakeData -> {
            //http://localhost:8081?username=test&password=test
            //例如果使用上面的链接进行connect,可以使用如下代码获取用户密码信息
            //String username = data.getSingleUrlParam("username");
            //String password = data.getSingleUrlParam("password");
            return true;
        });
        final SocketIOServer server = new SocketIOServer(config);
        return server;
    }

    @Bean
    public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
        return new SpringAnnotationScanner(socketServer);
    }
}

读取配置文件的util类,用于读取yml文件中的配置

public class YmlUtil {

    /**
     * key:文件名索引
     * value:配置文件内容
     */
    private static Map<String, LinkedHashMap> ymls = new HashMap<>();

    /**
     * string:当前线程需要查询的文件名
     */
    private static ThreadLocal<String> nowFileName = new ThreadLocal<>();

    /**
     * 加载配置文件
     * @param fileName
     */
    public static void loadYml(String fileName) {
        nowFileName.set(fileName);
        if (!ymls.containsKey(fileName)) {
            ymls.put(fileName, new Yaml().loadAs(YmlUtil.class.getResourceAsStream("/" + fileName), LinkedHashMap.class));
        }
    }

    public static Object getValueByKey(String key) throws Exception {
        // 首先将key进行拆分
        String[] keys = key.split("[.]");

        // 将配置文件进行复制
        Map ymlInfo = (Map) ymls.get(nowFileName.get()).clone();
        for (int i = 0; i < keys.length; i++) {
            Object value = ymlInfo.get(keys[i]);
            if (i < keys.length - 1) {
                ymlInfo = (Map) value;
            } else if (value == null) {
                throw new Exception("key不存在");
            } else {
                return value;
            }
        }
        throw new RuntimeException("不可能到这里的...");
    }

    public static Object getValue(String key) throws Exception {
        // 首先加载配置文件
        loadYml("application.yml");
        return getValueByKey(key);
    }
}

socketIo启动类

@Component
@Order(value=1)
public class ServerRunner implements CommandLineRunner {
    private final SocketIOServer server;

    @Autowired
    public ServerRunner(SocketIOServer server) {
        this.server = server;
    }

    @Override
    public void run(String... args) throws Exception {
        server.start();
        System.out.println("socket.io启动成功!");
    }
}

socketIo的前后端交互

@Component
public class SocketIoServer {

    public static SocketIOServer socketIoServer;

    @Autowired
    public SocketIoServer(SocketIOServer server) {
        this.socketIoServer = server;
    }

    @OnConnect
    public void onConnect(SocketIOClient client) {
        // TODO Auto-generated method stub
        String sa = client.getRemoteAddress().toString();
        String clientIp = sa.substring(1, sa.indexOf(":"));// 获取设备ip
        System.out.println(clientIp + "-------------------------" + "客户端已连接");
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        SocketIoServerMapUtil.put(clientIp, client);
    }

    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        // TODO Auto-generated method stub
        String sa = client.getRemoteAddress().toString();
        String clientIp = sa.substring(1, sa.indexOf(":"));// 获取设备ip
        System.out.println(clientIp + "-------------------------" + "客户端已断开连接");
        SocketIoServerMapUtil.remove(clientIp);
    }

    @OnEvent(value = "text")
    public void onEvent(SocketIOClient client, AckRequest ackRequest, String data) {
        // TODO Auto-generated method stub
        // 客户端推送advert_info事件时,onData接受数据,这里是string类型的json数据,还可以为Byte[],object其他类型
        String sa = client.getRemoteAddress().toString();
        String clientIp = sa.substring(1, sa.indexOf(":"));// 获取客户端连接的ip
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();// 获取客户端url参数
        System.out.println(clientIp + ":客户端:************" + data);
        JSONObject gpsData = (JSONObject) JSONObject.parse(data);
        String userIds = gpsData.get("userName") + "";
        String taskIds = gpsData.get("password") + "";
                client.sendEvent("text1", "后台得到了数据");
    }
}

存放前端连接的设备ip SocketIoServerMapUtil 类

public class SocketIoServerMapUtil {
    public static ConcurrentMap<String, SocketIOClient> webSocketMap = new ConcurrentHashMap<>();

    public static void put(String key, SocketIOClient SocketIOClient) {
        webSocketMap.put(key, SocketIOClient);
    }

    public static SocketIOClient get(String key) {
        return webSocketMap.get(key);
    }

    public static void remove(String key) {
        webSocketMap.remove(key);
    }

    public static Collection<SocketIOClient> getValues() {
        return webSocketMap.values();
    }
    
    public static ConcurrentMap<String, SocketIOClient> getWebSocketMap() {
        return webSocketMap;
    }
}

前端代码,这里需要我们前端项目中引进socket.io.js这个类。我这边介绍vue引入socket:npm install --save socket.io,不是vue环境的直接去下载一个js文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./socket.io.js"></script>
</head>
<body>
    <script>
    //创建socket
    var socket = io('http://localhost:9092');//用于连接后端的服务
    /*
    创建自定义事件 'news'
    作用:接受服务端 socket.emit('news', 数据); 发出的数据
    */
    socket.on('connect', function () {
        socket.emit('text', JSON.stringify({userName:"caiyy",passWord:"123456"}));
    });
    socket.on('text1', function (data) {
        //输出服务端响应了数据
        console.log(data);
    });
    </script>
</body>
</html>

现在所有的代码已经都准备好了,开始讲解详细步骤了。

开始导入socketIo的jar包,由于springboot存在一个启动类,我们这边的socketIo的配置虽然可以放在springboot的启动类中,但是一般情况下还是提取起来到一个类中。

 public static void main(String[] args) {
        SpringApplication.run(EventApplication.class,args);
    }

这里定义的是NettySocketConfig类。这个类中是socketIo的配置项,ipWin是window系统的ip,一般也就是本机测试的ip127.0.0.1;ipLinux是linux环境的ip,一般是我们正式环境的ip;port是socket的端口号,一般是9092,这些都是可以在application.yml中配置的,下面就是我在yml中的配置。

#网络ip
socketIo:
  win: localhost
  linux: 各自linux环境的ip
  port: 9092

然后就是配置socketIo的启动项即ServerRunner类。@Order(value=1)这个配置很重要,
这个标记包含一个value属性。属性接受整形值。如:1,2 等等。值越小拥有越高的优先级。就是因为我们springboot自带一个启动类,所以在这里配置启动的value值为1.就是在springboot启动之后在启动socketIo.启动完成配置也搞定了 ,现在就是要进行前后端的交互了。

@OnConnect用于监听客户端连接信息的,
@OnDisconnect用户监听客户端断开信息的。
@OnEvent(value = "text")用户后端监听前端的请求事件的。value值就是前端请求的唯一标识,前端携带这个请求的唯一标识进行请求后台,然后后台监听到这个请求,然后进行一系列操作。
client.sendEvent("text1", "后台得到了数据");用于后端响应前端数据。text1就是后台给前端的唯一标识,前端通过这个唯一标识来筛选后端给的数据是否是这个自己这个连接中所需要的。来确保消息不会发送给错误的前端连接者。
当后台服务启动之后,通过浏览器访问我们写的那个页面,后台就是看到想对应的ip连到后台服务当中,前端也会相对应的打印出“后台得到了数据”。

现在就在讲一个中间写了的一个没有用到的util类,SocketIoServerMapUtil 类。

这个类用户存储前端正在存于连接的ip,我们可以用这个类进行集体推送消息。只要前端连接到我们这个服务,在SocketIoServer里面的监听里面就用到了这个类的put方法。SocketIoServerMapUtil.put(clientIp, client);存入了客户端的ip。当我们的程序中某一个步骤启动了,要触发到集体推送消息的时候,我们可以在这个步骤中添加一段代码:

for (SocketIOClient client: SocketIoServerMapUtil.getValues()) {
                client.sendEvent("quntui", "新年快乐");
            }

然后在前端代码需要接受这个群推的登录者中加入以下代码就可以得到这个消息:

socket.on('quntui', function (data) {
        //输出服务端响应了数据
        alert(data);
    });

本次分享到此结束。。。欢迎大家留言评论和互相交流。
原文作者技术博客:https://www.jianshu.com/u/ac4daaeecdfe
95后前端妹子一枚,爱阅读,爱交友,将工作中遇到的问题记录在这里,希望给每一个看到的你能带来一点帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容