添加代码示例springboot-socketio
This commit is contained in:
@ -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