netty-socketio(一)之helloworld,与springboot整合

99封情书 提交于 2019-11-28 23:33:02

netty-socketio是一个开源的Socket.io服务器端的一个java的实现, 它基于Netty框架。

1、参考资料

  (1)netty-socketio项目github地址: https://github.com/mrniko/netty-socketio

  (2)netty-socketio-demo:https://github.com/mrniko/netty-socketio-demo

  (3)spring boot 下集成netty socket.io:https://blog.csdn.net/whyistao/article/details/79240445

 

2、helloworld案例:实现订阅/广播功能

本demo保存地址:https://github.com/wenbinouyang/oy_java

demo使用 springboot 2.1.4.RELEASE,项目总体结构:

  

  application.properties

logging.file=/home/wwwlogs/nettysocketio007/log.log

 

  pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.oy</groupId>
    <artifactId>nettysocketio007</artifactId>
    <version>0.0.1</version>
    <name>nettysocketio007</name>
    <description>nettysocketio007 for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

   springboot的启动类:

package com.oy;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;

import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;

@SpringBootApplication
@Order(1)
public class Nettysocketio007Application implements CommandLineRunner {
    private SocketIOServer server;

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

    @Bean
    public SocketIOServer socketIOServer() {
        Configuration config = new Configuration();
        config.setHostname("localhost");
        config.setPort(4001);
        this.server = new SocketIOServer(config);
        return server;
    }

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

    @Override
    public void run(String... args) throws Exception {
        server.start();
        UtilFunctions.log.info("socket.io run success!");
        
        // 向"channel_1" push数据
        Service.send(args);
    }

}

 

  MessageEventHandler类:

package com.oy;

import java.util.Set;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;

@Component
public class MessageEventHandler {
    public static SocketIOServer socketIoServer;

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

    @OnConnect
    public void onConnect(SocketIOClient client) {
        UUID socketSessionId = client.getSessionId();
        String ip = client.getRemoteAddress().toString();
        UtilFunctions.log.info("client connect, socketSessionId:{}, ip:{}", socketSessionId, ip);
    }

    @OnEvent("sub")
    public void sub(SocketIOClient client, AckRequest request, String channel) {
        UUID socketSessionId = client.getSessionId();
        String ip = client.getRemoteAddress().toString();
        client.joinRoom(channel);
        UtilFunctions.log.info("client sub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip);
    
        Set<String> rooms = client.getAllRooms();
        for (String room : rooms) {
            UtilFunctions.log.info("after client connect, room:{}", room);
        }
        
        // 客户端一订阅,就马上push一次
        sendAllEvent(Service.getMsg());
    }

//    @OnEvent("unsub")
//    public void unsub(SocketIOClient client, AckRequest request, String channel) {
//        UUID socketSessionId = client.getSessionId();
//        String ip = client.getRemoteAddress().toString();
//        client.leaveRoom(channel);
//        UtilFunctions.log.info("client unsub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip);
//    }

    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        UUID socketSessionId = client.getSessionId();
        String ip = client.getRemoteAddress().toString();
        UtilFunctions.log.info("client disconnect, socketSessionId:{}, ip:{}", socketSessionId, ip);
        
        Set<String> rooms = client.getAllRooms();
        for (String room : rooms) {
            UtilFunctions.log.info("after client disconnect, room:{}", room);
        }
    }

    // broadcast to channel "channel_1"
    public static void sendAllEvent(String data) {
        socketIoServer.getRoomOperations("channel_1").sendEvent("channel_1", data);
    }
}

 

   Service类:

package com.oy;

import com.alibaba.fastjson.JSONObject;

public class Service {
    private static String msg;

    // 向"channel_1" push数据
    public static void send(String[] args) throws Exception {
        int price = 0;
        if (args != null && args.length > 0) {
            try {
                price = Integer.parseInt(args[0]);
            } catch (Exception e) {
                UtilFunctions.log.info("args[0]不能转换为int");
                new Exception(e);
            }
        }

        for (int i = 0; i < 1000; i++) {
            JSONObject data = new JSONObject();
            data.put("current_price", price++);
            Service.msg = data.toJSONString(); // 把每次push的数据保存起来
            MessageEventHandler.sendAllEvent(data.toJSONString());
            Thread.sleep(1000 * 5);
        }
    }

    public static String getMsg() {
        return msg;
    }

    public static void setMsg(String msg) {
        Service.msg = msg;
    }
}

 

   UtilFunctions类:

package com.oy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UtilFunctions {
    public static Logger log = LoggerFactory.getLogger("nettyws007");
}

 

3、客户端html页面

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="socket.io.js"></script>
<style>
    body { 
        padding:20px;
    }
    #console {  
        overflow: auto; 
    }
    .username-msg {color:orange;}
    .connect-msg {color:green;}
    .disconnect-msg {color:red;}
    .send-msg {color:#888}
</style>
</head>

<body>
    <div id="console" class="well">
    </div><br/><br/>
   
    current_price:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <span id="current_price"></span><br/>
    
</body>
         
<script type="text/javascript">
    var socket =  io.connect('http://localhost:4001', {
        transports:['websocket']
    });
        
    socket.on('connect', function() {
        console.log("msg页面连接成功!");
        socket.emit('sub', "channel_1");
        output('<span class="connect-msg">Client has connected to the server!</span>');
        output('<span class="connect-msg">Client send {"event": "sub", "channel": "channel_1"}</span>');
    });
    
    socket.on('disconnect', function() {
        output('<span class="disconnect-msg">The client has disconnected!</span>');
    });

    socket.on('channel_1', function(data) {
        var jsonObj = eval("(" + data + ")");
        console.log("收到cfd_md的消息:" + data);
        $("#current_price").html(jsonObj.current_price);
    });
    
             
    function output(message) {
        var currentTime = "<span class='time'>" +  NowTime() + "</span>";
        var element = $("<div>" + currentTime + " " + message + "</div>");
        $('#console').prepend(element);
    }
        
    // 获取当前时间
    function NowTime() {
        var time=new Date();
        var year=time.getFullYear();//获取年
        var month=time.getMonth()+1;//或者月
        var day=time.getDate();//或者天
        var hour=time.getHours();//获取小时
        var minu =time.getMinutes();//获取分钟
        var second=time.getSeconds();//或者秒
        var data=year+"-";
        if(month<10){
            data+="0";
        }
        data+=month+"-";
        if(day<10){
            data+="0"
        }
        data+=day+" ";
        if(hour<10){
            data+="0"
        }
        data+=hour+":";
        if(minu<10){
            data+="0"
        }
        data+=minu+":";
        if(second<10){
            data+="0"
        }
        data+=second;
        return data;
    }
        
</script>
</html>

 

 4、测试结果

  4.1、控制台结果:

  从控制台结果可以看出,有一个默认为" "的room;当客户端关闭时,将会从所有的room中离开。

 

   4.2、下载一个socket.io.js,与下面的html页面保存在一个文件夹下。双击html测试页面,F12打开开发者工具。浏览器结果:

 

5、将项目打成可执行jar包

  选中项目,右键Run As, Maven build...,运行clean package命令。如果报错,可能是src/test/java里面的代码的问题,删除即可。或者可以使用clean package -DskipTests跳过测试过程,并将项目打包。

 

  选中项目,右键Properties,

 

   可以将打好的jar重命名为a.jar(名字随意)。shift+右键,在此处打开Powershell,输入命令java -jar a.jar即可启动socket服务。

 

 

6、将jar上传到linux服务器,并且注册一个服务,专门用来开启jar提供的socket服务

  (1)将a.jar重命名为nettysocketio007.jar,上传到linux的/home/socketio/目录下。

  (2)进入到/etc/systemd/system

  

  (3)vim nettysocketio007.service,输入如下内容:

[Unit]
Description=nettysocketio007 Service

[Service]
Type=simple
ExecStart=/usr/bin/java -jar -server /home/socketio/nettysocketio007.jar
StandardOutput=null
WorkingDirectory=/home/socketio
Restart=on-failure

[Install]
WantedBy=multi-user.target
Alias=nettysocketio007.service

  (4)开启服务:systemctl start nettysocketio007,相当于执行了命令:/usr/bin/java -jar -server /home/socketio/nettysocketio007.jar

  注意:

  a、/usr/bin/java是我服务器上java命令的path,如果将JAVA_HOME/bin配置进了path,直接java -jar -server /home/socketio/nettysocketio007.jar即可。

   b、WorkingDirectory=/home/socketio指定jar包的位置。

  (5)查看服务:systemctl status nettysocketio007

  (6)停止服务:systemctl stop nettysocketio007。停止服务后最好等一小会再开启服务,否则可能报Failed to execute CommandLineRunner。

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!