添加代码示例springboot-socketio
This commit is contained in:
parent
74757c1628
commit
7db9f11a9c
@ -13,7 +13,7 @@ Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身
|
|||||||
|
|
||||||
## 子项目列表
|
## 子项目列表
|
||||||
|
|
||||||
每个项目一篇博客文章的详细讲解,点击链接可以查看文章:
|
每个项目一篇博客文章的详细讲解,点击链接可以查看文章 :point_right:
|
||||||
|
|
||||||
项目名称 | 文章地址
|
项目名称 | 文章地址
|
||||||
----------------------------|------------------------------------------------------------------------------------------
|
----------------------------|------------------------------------------------------------------------------------------
|
||||||
@ -40,7 +40,7 @@ springboot-batch | [批处理](https://www.xncoding.com/2017/08/01/sp
|
|||||||
springboot-rabbitmq | [集成消息队列RabbitMQ](https://www.xncoding.com/2017/08/06/spring/sb-rabbitmq.html)
|
springboot-rabbitmq | [集成消息队列RabbitMQ](https://www.xncoding.com/2017/08/06/spring/sb-rabbitmq.html)
|
||||||
springboot-echarts | [集成Echarts导出图片](https://www.xncoding.com/2017/08/19/spring/sb-echarts.html)
|
springboot-echarts | [集成Echarts导出图片](https://www.xncoding.com/2017/08/19/spring/sb-echarts.html)
|
||||||
app-manage | [一个完整的Web后台管理系统,组合使用了多种技术]
|
app-manage | [一个完整的Web后台管理系统,组合使用了多种技术]
|
||||||
app-manage-api | [同时实现了需要认证授权访问的RESTful API接口和WebSocket接口]
|
app-manage-api | [实现需要认证授权访问的RESTful API接口和WebSocket接口]
|
||||||
|
|
||||||
## 环境
|
## 环境
|
||||||
|
|
||||||
|
17
springboot-socketio/.gitignore
vendored
Normal file
17
springboot-socketio/.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# 此为注释– 将被Git 忽略
|
||||||
|
# /结尾表示是目录,忽略目录和目录下的所有件
|
||||||
|
# /开头表示根目录,否则是.gitignore的相对目录
|
||||||
|
# !开头表示反选
|
||||||
|
.idea/
|
||||||
|
target/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
*.log
|
||||||
|
.svn/
|
||||||
|
.project
|
||||||
|
rebel.xml
|
||||||
|
.rebel-remote.xml.*
|
||||||
|
swagger.json
|
||||||
|
swagger.adoc
|
||||||
|
|
20
springboot-socketio/LICENSE
Normal file
20
springboot-socketio/LICENSE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Xiong Neng
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
39
springboot-socketio/README.md
Normal file
39
springboot-socketio/README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
在SpringBoot中有两种实现WebSocket实时通信的方式:
|
||||||
|
|
||||||
|
1. 一个是使用WebSocket的一个子协议stomp
|
||||||
|
2. 另外一个是使用Socket.IO协议实现。
|
||||||
|
|
||||||
|
本项目演示如何通过Socket.IO协议实现
|
||||||
|
|
||||||
|
## WeSocket长连接
|
||||||
|
|
||||||
|
Socket.IO服务器端和客户端,使用Java语言实现,用于和浏览器或Android客户端进行长连接的WebSocket通信。
|
||||||
|
服务器端可以实时将消息推送给客户端,以此来取代客户端轮询机制。
|
||||||
|
|
||||||
|
Sockt.io官网:<https://socket.io/>
|
||||||
|
|
||||||
|
服务器端使用 [netty-socketio](https://github.com/mrniko/netty-socketio)
|
||||||
|
|
||||||
|
客户端使用 [socket.io-client-java](https://github.com/socketio/socket.io-client-java)
|
||||||
|
|
||||||
|
### WebSocket js客户端测试
|
||||||
|
|
||||||
|
```
|
||||||
|
/client/html/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Java客户端测试
|
||||||
|
|
||||||
|
```
|
||||||
|
com.xncoding.pos.socket.client.SocketClient
|
||||||
|
```
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
Copyright (c) 2018 Xiong Neng
|
||||||
|
|
||||||
|
基于 MIT 协议发布: <http://www.opensource.org/licenses/MIT>
|
||||||
|
|
137
springboot-socketio/pom.xml
Normal file
137
springboot-socketio/pom.xml
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<groupId>com.xncoding</groupId>
|
||||||
|
<artifactId>springboot-socketio</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>springboot-socketio</name>
|
||||||
|
<description>集成SocketIO实时通信</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>1.5.9.RELEASE</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<netty.version>4.1.19.Final</netty.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- netty-socketio-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.corundumstudio.socketio</groupId>
|
||||||
|
<artifactId>netty-socketio</artifactId>
|
||||||
|
<version>1.7.13</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-buffer</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-codec</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-codec-http</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-common</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-handler</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-resolver</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport-native-epoll</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-transport-native-unix-common</artifactId>
|
||||||
|
<version>${netty.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.socket</groupId>
|
||||||
|
<artifactId>socket.io-client</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.6.1</version>
|
||||||
|
<configuration>
|
||||||
|
<!--<proc>none</proc>-->
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.20</version>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<swaggerOutputDir>${project.basedir}/src/main/resources/swagger</swaggerOutputDir>
|
||||||
|
<asciiDocOutputDir>${project.basedir}/src/main/resources/swagger/swagger</asciiDocOutputDir>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
<skip>true</skip>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
72
springboot-socketio/run.sh
Normal file
72
springboot-socketio/run.sh
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 项目自动更新脚本
|
||||||
|
# 先clone相应的分支下来:
|
||||||
|
# git clone ssh://git@120.24.173.142:7999/xxx.git
|
||||||
|
# 远程调试启动:
|
||||||
|
# nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
|
||||||
|
|
||||||
|
function start {
|
||||||
|
profile="$1"
|
||||||
|
echo "启动环境profile=${profile}"
|
||||||
|
jarfile=$(ls target/*.jar)
|
||||||
|
if [[ "$?" == "0" ]]; then
|
||||||
|
stop $profile $jarfile
|
||||||
|
fi
|
||||||
|
branch=$(git branch |awk '{print $2}')
|
||||||
|
git pull origin ${branch}
|
||||||
|
echo "更新完代码开始重新打包"
|
||||||
|
mvn clean && mvn clean && mvn package -DskipTests=true
|
||||||
|
if [[ "$?" != "0" ]]; then
|
||||||
|
echo "编译出错,退出!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &"
|
||||||
|
nohup java -Xms512m -Xmx1024m -jar -Dspring.profiles.active=${profile} ${jarfile} >/dev/null 2>&1 &
|
||||||
|
echo "启动应用中,请查看日志文件..."
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop {
|
||||||
|
profile="$1"
|
||||||
|
jarfile="$2"
|
||||||
|
ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep > /dev/null
|
||||||
|
if [[ "$?" == "0" ]]; then
|
||||||
|
echo "该应用还在跑,我先停了它"
|
||||||
|
pid=$(ps aux | grep "${jarfile}" | grep "spring.profiles.active=${profile}" | grep -v grep |awk '{print $2}')
|
||||||
|
if [[ "$pid" != "" ]]; then
|
||||||
|
kill -9 $pid
|
||||||
|
fi
|
||||||
|
echo "停止应用成功..."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$1" == "start" ]]; then
|
||||||
|
if [[ "$#" < 2 ]]; then
|
||||||
|
echo "请输入正确参数:./epay.sh start {profile}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
profile="$2"
|
||||||
|
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
|
||||||
|
echo "参数错误,请输入正确的profile参数,使用方法:"
|
||||||
|
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值:dev|test|show|production"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
start "${profile}"
|
||||||
|
elif [[ "$1" == "stop" ]]; then
|
||||||
|
if [[ "$#" < 2 ]]; then
|
||||||
|
echo "请输入正确参数:./epay.sh stop {profile}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
profile="$2"
|
||||||
|
if [[ "$profile" != "dev" && "$profile" != "test" && "$profile" != "show" && "$profile" != "production" ]]; then
|
||||||
|
echo "参数错误,请输入正确的profile参数,使用方法:"
|
||||||
|
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值:dev|test|show|production"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
jarfile=$(ls target/*.jar)
|
||||||
|
stop $profile $jarfile
|
||||||
|
else
|
||||||
|
echo "参数错误,使用方法:{}参数是必填的,[]参数可选"
|
||||||
|
echo "./epay.sh start {profile} ==> 启动应用,{profile}取值:dev|test|show|production"
|
||||||
|
echo "./epay.sh stop {profile} ==> 停止应用,{profile}取值:dev|test|show|production"
|
||||||
|
exit 1
|
||||||
|
fi
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.xncoding.jwt;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(Application.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package com.xncoding.jwt.common;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象和Json转换器
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/18
|
||||||
|
*/
|
||||||
|
public class JsonConverter {
|
||||||
|
public static JSONObject objectToJSONObject(Object object) {
|
||||||
|
try {
|
||||||
|
String jsonString = new ObjectMapper().writeValueAsString(object);
|
||||||
|
return new JSONObject(jsonString);
|
||||||
|
} catch (JSONException | JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONArray objectToJSONArray(Object object) {
|
||||||
|
try {
|
||||||
|
String jsonString = new ObjectMapper().writeValueAsString(object);
|
||||||
|
return new JSONArray(jsonString);
|
||||||
|
} catch (JSONException | JsonProcessingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T jsonObjectToObject(Object jsonObject, Class<T> clazz) {
|
||||||
|
try {
|
||||||
|
// List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});
|
||||||
|
return new ObjectMapper().readValue(jsonObject.toString(), clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.xncoding.jwt.common;
|
||||||
|
|
||||||
|
import com.corundumstudio.socketio.SocketIOServer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpringBoot启动之后执行
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2017/7/31
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Order(1)
|
||||||
|
public class ServerRunner implements CommandLineRunner {
|
||||||
|
private final SocketIOServer server;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ServerRunner.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ServerRunner(SocketIOServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
logger.info("ServerRunner 开始启动啦...");
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.xncoding.jwt.config;
|
||||||
|
|
||||||
|
import com.corundumstudio.socketio.SocketIOServer;
|
||||||
|
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
|
||||||
|
import com.xncoding.jwt.config.properties.MyProperties;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NettySocketConfig
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/19
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class NettySocketConfig {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MyProperties myProperties;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(NettySocketConfig.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SocketIOServer socketIOServer() {
|
||||||
|
/*
|
||||||
|
* 创建Socket,并设置监听端口
|
||||||
|
*/
|
||||||
|
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
|
||||||
|
// 设置主机名,默认是0.0.0.0
|
||||||
|
// config.setHostname("localhost");
|
||||||
|
// 设置监听端口
|
||||||
|
config.setPort(myProperties.getSocketPort());
|
||||||
|
// 协议升级超时时间(毫秒),默认10000。HTTP握手升级为ws协议超时时间
|
||||||
|
config.setUpgradeTimeout(10000);
|
||||||
|
// Ping消息间隔(毫秒),默认25000。客户端向服务器发送一条心跳消息间隔
|
||||||
|
config.setPingInterval(myProperties.getPingInterval());
|
||||||
|
// Ping消息超时时间(毫秒),默认60000,这个时间间隔内没有接收到心跳消息就会发送超时事件
|
||||||
|
config.setPingTimeout(myProperties.getPingTimeout());
|
||||||
|
// 握手协议参数使用JWT的Token认证方案
|
||||||
|
config.setAuthorizationListener(data -> {
|
||||||
|
// 可以使用如下代码获取用户密码信息
|
||||||
|
String token = data.getSingleUrlParam("token");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return new SocketIOServer(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) {
|
||||||
|
return new SpringAnnotationScanner(socketServer);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.xncoding.jwt.config.properties;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本项目自定义配置
|
||||||
|
*
|
||||||
|
* @author xiongneng
|
||||||
|
* @since 2018/01/06 21:09
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "xncoding")
|
||||||
|
public class MyProperties {
|
||||||
|
/**
|
||||||
|
* socket端口
|
||||||
|
*/
|
||||||
|
private Integer socketPort;
|
||||||
|
/**
|
||||||
|
* Ping消息间隔(毫秒)
|
||||||
|
*/
|
||||||
|
private Integer pingInterval;
|
||||||
|
/**
|
||||||
|
* Ping消息超时时间(毫秒)
|
||||||
|
*/
|
||||||
|
private Integer pingTimeout;
|
||||||
|
/**
|
||||||
|
* APK文件访问URL前缀
|
||||||
|
*/
|
||||||
|
private String apkUrlPrefix;
|
||||||
|
|
||||||
|
public Integer getSocketPort() {
|
||||||
|
return socketPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSocketPort(Integer socketPort) {
|
||||||
|
this.socketPort = socketPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPingInterval() {
|
||||||
|
return pingInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPingInterval(Integer pingInterval) {
|
||||||
|
this.pingInterval = pingInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPingTimeout() {
|
||||||
|
return pingTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPingTimeout(Integer pingTimeout) {
|
||||||
|
this.pingTimeout = pingTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApkUrlPrefix() {
|
||||||
|
return apkUrlPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApkUrlPrefix(String apkUrlPrefix) {
|
||||||
|
this.apkUrlPrefix = apkUrlPrefix;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.xncoding.jwt.handler;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import com.xncoding.jwt.model.ChatMessage;
|
||||||
|
import com.xncoding.jwt.model.LoginRequest;
|
||||||
|
import io.socket.client.Socket;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息事件处理器
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/19
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MessageEventHandler {
|
||||||
|
|
||||||
|
private final SocketIOServer server;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MessageEventHandler.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public MessageEventHandler(SocketIOServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加connect事件,当客户端发起连接时调用
|
||||||
|
@OnConnect
|
||||||
|
public void onConnect(SocketIOClient client) {
|
||||||
|
if (client != null) {
|
||||||
|
String username = client.getHandshakeData().getSingleUrlParam("username");
|
||||||
|
String password = client.getHandshakeData().getSingleUrlParam("password");
|
||||||
|
String sessionId = client.getSessionId().toString();
|
||||||
|
logger.info("连接成功, username=" + username + ", password=" + password + ", sessionId=" + sessionId);
|
||||||
|
} else {
|
||||||
|
logger.error("客户端为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加@OnDisconnect事件,客户端断开连接时调用,刷新客户端信息
|
||||||
|
@OnDisconnect
|
||||||
|
public void onDisconnect(SocketIOClient client) {
|
||||||
|
logger.info("客户端断开连接, sessionId=" + client.getSessionId().toString());
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息接收入口
|
||||||
|
@OnEvent(value = "chatevent")
|
||||||
|
public void onEvent(SocketIOClient client, AckRequest ackRequest, ChatMessage chat) {
|
||||||
|
logger.info("接收到客户端消息");
|
||||||
|
if (ackRequest.isAckRequested()) {
|
||||||
|
// send ack response with data to client
|
||||||
|
ackRequest.sendAckData("服务器回答chatevent, userName=" + chat.getUserName() + ",message=" + chat.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录接口
|
||||||
|
@OnEvent(value = "login")
|
||||||
|
public void onLogin(SocketIOClient client, AckRequest ackRequest, LoginRequest message) {
|
||||||
|
logger.info("接收到客户端登录消息");
|
||||||
|
if (ackRequest.isAckRequested()) {
|
||||||
|
// send ack response with data to client
|
||||||
|
ackRequest.sendAckData("服务器回答login", message.getCode(), message.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.xncoding.jwt.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChatMessage
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/30
|
||||||
|
*/
|
||||||
|
public class ChatMessage {
|
||||||
|
private String userName;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.xncoding.jwt.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LoginRequest
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/18
|
||||||
|
*/
|
||||||
|
public class LoginRequest implements Serializable {
|
||||||
|
private int code;
|
||||||
|
private String body;
|
||||||
|
|
||||||
|
public LoginRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoginRequest(int code, String body) {
|
||||||
|
super();
|
||||||
|
this.code = code;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(String body) {
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
45
springboot-socketio/src/main/resources/application.yml
Normal file
45
springboot-socketio/src/main/resources/application.yml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
##########################################################
|
||||||
|
################## 所有profile共有的配置 #################
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
################### 自定义项目配置 ###################
|
||||||
|
xncoding:
|
||||||
|
socket-port: 9099 #socket端口
|
||||||
|
ping-interval: 60000 #Ping消息间隔(毫秒)
|
||||||
|
ping-timeout: 180000 #Ping消息超时时间(毫秒)
|
||||||
|
|
||||||
|
################### spring配置 ###################
|
||||||
|
spring:
|
||||||
|
profiles:
|
||||||
|
active: dev
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
######################## 开发环境profile ##########################
|
||||||
|
#####################################################################
|
||||||
|
spring:
|
||||||
|
profiles: dev
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
ROOT: INFO
|
||||||
|
com:
|
||||||
|
xncoding: DEBUG
|
||||||
|
file: E:/logs/app.log
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
######################## 测试环境profile ##########################
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
spring:
|
||||||
|
profiles: test
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
ROOT: INFO
|
||||||
|
com:
|
||||||
|
xncoding: DEBUG
|
||||||
|
file: /var/logs/app.log
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.xncoding.jwt.socket.client;
|
||||||
|
|
||||||
|
import com.xncoding.jwt.common.JsonConverter;
|
||||||
|
import com.xncoding.jwt.model.LoginRequest;
|
||||||
|
import io.socket.client.Ack;
|
||||||
|
import io.socket.client.IO;
|
||||||
|
import io.socket.client.Socket;
|
||||||
|
import io.socket.emitter.Emitter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SocketClient
|
||||||
|
*
|
||||||
|
* @author XiongNeng
|
||||||
|
* @version 1.0
|
||||||
|
* @since 2018/1/18
|
||||||
|
*/
|
||||||
|
public class SocketClient {
|
||||||
|
private static Socket socket;
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(SocketClient.class);
|
||||||
|
|
||||||
|
public static void main(String[] args) throws URISyntaxException {
|
||||||
|
IO.Options options = new IO.Options();
|
||||||
|
options.transports = new String[]{"websocket"};
|
||||||
|
options.reconnectionAttempts = 2;
|
||||||
|
options.reconnectionDelay = 1000; // 失败重连的时间间隔(ms)
|
||||||
|
options.timeout = 20000; // 连接超时时间(ms)
|
||||||
|
options.forceNew = true;
|
||||||
|
options.query = "username=test1&password=test1";
|
||||||
|
socket = IO.socket("http://localhost:9099/", options);
|
||||||
|
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
// 客户端一旦连接成功,开始发起登录请求
|
||||||
|
LoginRequest message = new LoginRequest(12, "这是客户端消息体");
|
||||||
|
socket.emit("login", JsonConverter.objectToJSONObject(message), (Ack) args1 -> {
|
||||||
|
logger.info("回执消息=" + Arrays.stream(args1).map(Object::toString).collect(Collectors.joining(",")));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.info("Socket.EVENT_CONNECT_ERROR");
|
||||||
|
socket.disconnect();
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_CONNECT_TIMEOUT, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.info("Socket.EVENT_CONNECT_TIMEOUT");
|
||||||
|
socket.disconnect();
|
||||||
|
}
|
||||||
|
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
|
||||||
|
@Override
|
||||||
|
public void call(Object... args) {
|
||||||
|
logger.info("客户端断开连接啦。。。");
|
||||||
|
socket.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
}
|
4983
springboot-socketio/src/test/java/com/xncoding/jwt/socket/client/html/bootstrap.css
vendored
Normal file
4983
springboot-socketio/src/test/java/com/xncoding/jwt/socket/client/html/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,110 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
|
||||||
|
<title>Demo Chat</title>
|
||||||
|
|
||||||
|
<link href="bootstrap.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#console {
|
||||||
|
height: 400px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username-msg {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.connect-msg {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnect-msg {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-msg {
|
||||||
|
color: #888
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="js/socket.io/socket.io.js"></script>
|
||||||
|
<script src="js/moment.min.js"></script>
|
||||||
|
<script src="js/jquery-1.10.1.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var userName = 'user' + Math.floor((Math.random() * 1000) + 1);
|
||||||
|
|
||||||
|
var socket = io.connect('http://127.0.0.1:9099?username=' + userName + '&password=123456');
|
||||||
|
|
||||||
|
socket.on('connect', function () {
|
||||||
|
output('<span class="connect-msg">Client has connected to the server!</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('chatevent', function (data) {
|
||||||
|
output('<span class="username-msg">' + data.userName + ':</span> ' + data.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', function () {
|
||||||
|
output('<span class="disconnect-msg">The client has disconnected!</span>');
|
||||||
|
});
|
||||||
|
|
||||||
|
function sendDisconnect() {
|
||||||
|
socket.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessage() {
|
||||||
|
var message = $('#msg').val();
|
||||||
|
$('#msg').val('');
|
||||||
|
|
||||||
|
var jsonObject = {
|
||||||
|
userName: userName,
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
socket.emit('chatevent', jsonObject, function (data) {
|
||||||
|
output('<span class="username-msg">' + data + '</span> ');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function output(message) {
|
||||||
|
var currentTime = "<span class='time'>" + moment().format('HH:mm:ss.SSS') + "</span>";
|
||||||
|
var element = $("<div>" + currentTime + " " + message + "</div>");
|
||||||
|
$('#console').prepend(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).keydown(function (e) {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
$('#send').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Netty-socketio Demo Chat</h1>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div id="console" class="well">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="well form-inline" onsubmit="return false;">
|
||||||
|
<input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>
|
||||||
|
<button type="button" onClick="sendMessage()" class="btn" id="send">Send</button>
|
||||||
|
<button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
File diff suppressed because one or more lines are too long
6
springboot-socketio/src/test/java/com/xncoding/jwt/socket/client/html/js/moment.min.js
vendored
Normal file
6
springboot-socketio/src/test/java/com/xncoding/jwt/socket/client/html/js/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user