添加代码示例springboot-socketio

This commit is contained in:
yidao620
2018-02-28 15:23:38 +08:00
parent 74757c1628
commit 7db9f11a9c
21 changed files with 5864 additions and 2 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View 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

View File

@ -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();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long