添加代码示例springboot-socketio
This commit is contained in:
@ -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-echarts | [集成Echarts导出图片](https://www.xncoding.com/2017/08/19/spring/sb-echarts.html)
|
||||
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
Reference in New Issue
Block a user