netty-socketio(一)之helloworld,与springboot整合
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:
<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。
---
netty-socketio(一)之helloworld,与springboot整合的更多相关文章
- SpringBoot整合Netty并使用Protobuf进行数据传输(附工程)
前言 本篇文章主要介绍的是SpringBoot整合Netty以及使用Protobuf进行数据传输的相关内容.Protobuf会简单的介绍下用法,至于Netty在之前的文章中已经简单的介绍过了,这里就不 ...
- springboot整合netty(二)
目录 前言 正文 代码 1. 新建一个springboot项目,在pom文件中添加netty依赖: 2.新建netty服务 3.netty调用所需的服务类 4 springboot启动类 5.测试 我 ...
- SpringBoot整合elasticsearch(三)
Docker安装elasticsearch 启动注意2点,1是内存,2是线程数(此处进行简单安装,后面会详细补充es文档) [root@topcheer ~]# docker images REPOS ...
- 一篇学习完rabbitmq基础知识,springboot整合rabbitmq
一 rabbitmq 介绍 MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced MessageQueue 高级消息队列协议 ...
- 【SpringBoot系列5】SpringBoot整合RabbitMQ
前言: 因为项目需要用到RabbitMQ,前几天就看了看RabbitMQ的知识,记录下SpringBoot整合RabbitMQ的过程. 给出两个网址: RabbitMQ官方教程:http://www. ...
- SpringBoot整合Slf4j+logback日志框架
一.Slf4j简单介绍与优势 1.介绍 Slf4j的全称是Simple Loging Facade For Java(Java简单日志门面),它仅仅是一个为Java程序提供日志输出的统一接口,并不是一 ...
- SpringBoot整合Redis使用Restful风格实现CRUD功能
前言 本篇文章主要介绍的是SpringBoot整合Redis,使用Restful风格实现的CRUD功能. Redis 介绍 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-valu ...
- SpringBoot整合Swagger和Actuator
前言 本篇文章主要介绍的是SpringBoot整合Swagger(API文档生成框架)和SpringBoot整合Actuator(项目监控)使用教程. SpringBoot整合Swagger 说明:如 ...
- SpringBoot整合MyBatis例子
1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- Springboot整合Elasticsearch报错availableProcessors is already set to [4], rejecting [4]
Springboot整合Elasticsearch报错 今天使用SpringBoot整合Elasticsearch时候,相关的配置完成后,启动项目就报错了. nested exception is j ...
随机推荐
- Kubernetes实战(一):k8s v1.11.x v1.12.x 高可用安装
说明:部署的过程中请保证每个命令都有在相应的节点执行,并且执行成功,此文档已经帮助几十人(仅包含和我取得联系的)快速部署k8s高可用集群,文档不足之处也已更改,在部署过程中遇到问题请先检查是否遗忘某个 ...
- 4、 LwIP协议栈规范翻译——流程模型
4.流程模型 协议实现的流程模型描述了系统被划分为不同的流程的方式.用于实现通信协议的一个流程模型是让每个协议作为一个独立的进程运行.有了这个模型,严格的协议分层被强制执行,并且协议之间的通信点必须严 ...
- 从零开始一起学习SLAM | 点云平滑法线估计
点击公众号"计算机视觉life"关注,置顶星标更快接收消息! 本文编程练习框架及数据获取方法见文末获取方式 菜单栏点击"知识星球"查看「从零开始学习SLAM」一 ...
- 从Joda-Time反观Java语言利弊
基本上每个企业应用系统都涉及到时间处理.我们知道,以前用java原生的Date+Calendar非常的不方便.后来Joda-Time诞生,这个专门处理日期/时间的库提供了DateTime类型,用它可以 ...
- poj3155 最大密度子图
求最大密度子图 记得在最后一次寻找的时候记得将进入的边放大那么一点点,这样有利于当每条边都满流的情况下会选择点 #include <iostream> #include <algor ...
- Flask最强攻略 - 跟DragonFire学Flask - 第十六篇 Flask-Migrate
终于到了Flask-Migrate,之前在学习Flask-SQLAlchemy的时候,有的同学就提过类似的问题,Flask支持 makemigration / migrate 吗? 答案在这里该诉你, ...
- kubernetes1.4新特性(一):支持sysctl命令
sysctl是一个允许改变正在运行中的Linux系统内核参数的接口.可以通过sysctl修改Linux系统内核中的TCP/IP 堆栈和虚拟内存系统的高级选项,而且不需要重新启动Linux系统,就可以实 ...
- 设计模式理解(九)结构型——外观(Facade)
等了好久,终于想起来开写了,这次写的是外观模式,记得大学时弄课程设计,外观模式搞得我比较混乱,因为单词不认识,后来觉得有点蛋疼,感觉是一坨混乱的东西然后加个壳再弄几个外部调用的接口而已.个人认为,Fa ...
- 内置函数filter()和匿名函数lambda解析
一.内置函数filter filter()函数是 Python 内置的一个高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回由符合条件迭代器 ...
- [Python]基础教程(2)、PyCharm安装及中文编码
一.PyCharm安装 http://blog.csdn.net/yctjin/article/details/70307933?locationNum=11&fps=1 这篇文章写得及其详细 ...