配置依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.19</version>
</dependency>
后台参数
socketio:
port: 8081
host: 0.0.0.0
boss-count: 1
work-count: 100
max-frame-payload-length: 1048576
max-http-content-length: 1048576
allow-custom-requests: true
upgrade-timeout: 10000
ping-timeout: 60000
ping-interval: 25000
@Component
@PropertySource("classpath:application.yml")
@ConfigurationProperties(prefix = "socketio")
@Data
public class SocketIO {
/**
* 请求主机
*/
private String host;
/**
* 请求主机端口
*/
private Integer port;
/**
* 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
*/
private Integer maxFramePayloadLength;
/**
* 设置http交互最大内容长度
*/
private Integer maxHttpContentLength;
/**
* socket连接数大小(如只监听一个端口boss线程组为1即可)
*/
private Integer bossCount;
/**
* 工作线程
*/
private Integer workCount;
/**
* 允许自定义请求
*/
private Boolean allowCustomRequests;
/**
* 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
*/
private Integer upgradeTimeout;
/**
* Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
*/
private Integer pingTimeout;
/**
* Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
*/
private Integer pingInterval;
}
- Configuration 类,将socketIO注册为springboot 的容器
import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.liheji.component.SocketIOHandler;
import top.liheji.properties.SocketIO;
/**
* @Time : 2021/11/25 23:46
* @Author : Galaxy
* @Create : IdeaJ
* @Project : server
* @Description :
*/
@Configuration
public class SocketIOConfig {
@Autowired
public SocketIO socketIO;
/**
* 配置装载
*
* @return SocketIOServer
*/
@Bean
public SocketIOServer socketIOServer() {
SocketConfig socketConfig = new SocketConfig();
socketConfig.setTcpNoDelay(true);
socketConfig.setSoLinger(0);
com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
config.setSocketConfig(socketConfig);
config.setHostname(socketIO.getHost());
config.setPort(socketIO.getPort());
config.setBossThreads(socketIO.getBossCount());
config.setWorkerThreads(socketIO.getWorkCount());
config.setAllowCustomRequests(socketIO.getAllowCustomRequests());
config.setUpgradeTimeout(socketIO.getUpgradeTimeout());
config.setPingTimeout(socketIO.getPingTimeout());
config.setPingInterval(socketIO.getPingInterval());
//权限认证拦截器
config.setAuthorizationListener(socketIOHandler());
return new SocketIOServer(config);
}
/**
* 用于扫描netty-socketio的注解,比如 @OnConnect、@OnEvent
**/
@Bean
public SpringAnnotationScanner springAnnotationScanner() {
return new SpringAnnotationScanner(socketIOServer());
}
/**
* 注入socket处理拦截器
*
* @return 拦截器
*/
@Bean
public SocketIOHandler socketIOHandler() {
return new SocketIOHandler();
}
}
- Configuration 用到了权限认证拦截器如下
import com.corundumstudio.socketio.AuthorizationListener;
import com.corundumstudio.socketio.HandshakeData;
public class SocketIOHandler implements AuthorizationListener {
@Override
public boolean isAuthorized(HandshakeData handshakeData) {
String token = handshakeData.getSingleUrlParam("token");
if ("e4038260a69e45159beaf7a282dd8e82".equals(token)) {
return true;
}
return false;
}
}
服务类
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.liheji.service.SocketIOService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
//注册为服务
@Service
public class SocketIOService {
/**
* 已连接的客户端
*/
private static final Map<UUID, SocketIOClient> CLIENT_MAP = new ConcurrentHashMap<>();
/**
* 注入socketIOServer
*/
@Autowired
private SocketIOServer socketIOServer;
/**
* 客户端连上socket服务器时执行此事件
*
* @param client 客户端
*/
@OnConnect
public void onConnect(SocketIOClient client) {
String server = client.getHandshakeData().getSingleUrlParam("server");
String key = client.getHandshakeData().getSingleUrlParam("key");
System.out.println("认证");
System.out.println(server);
System.out.println(key);
System.out.println("连接成功: " + client.getSessionId());
System.out.println("连接成功: " + client.getTransport());
CLIENT_MAP.put(client.getSessionId(), client);
}
/**
* 客户端断开socket服务器时执行此事件
*
* @param client 客户端
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
CLIENT_MAP.remove(client.getSessionId());
System.out.println("断开连接: " + client.getSessionId());
client.disconnect();
}
//注册json事件
@OnEvent("json")
public void onEventJson(SocketIOClient client, AckRequest request, Object data) {
System.out.println("收到来自客户端:" + data);
Map<String, Object> map = new HashMap<>(2);
map.put("code", 0);
map.put("msg", "更新完成");
map.put("count", 1);
map.put("total", 1);
client.sendEvent("push", map);
}
/**
* 在加载当前Bean之后执行
* 启动监听客户端连接
*/
@PostConstruct
private void autoStart() {
socketIOServer.start();
}
/**
* 销毁Bean之前执行
* 停止监听客户端连接
* 防止端口占用
*/
@PreDestroy
private void autoStop() {
if (socketIOServer != null) {
socketIOServer.stop();
socketIOServer = null;
}
CLIENT_MAP.clear();
}
}
前端配置
<!--jquery依赖-->
<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script>
<!--socket.io核心依赖-->
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
let socket;
function connect() {
//token 用于权限验证
//transports 传输类型,指定使用websocket
socket = io.connect('http://127.0.0.1:8081', {
query: `token=e4038260a69e45159beaf7a282dd8e82`,
transports: ["websocket", "polling"]
});
//连接建立事件
socket.on('connect', function () {
console.log("连接成功");
serverOutput('<span class="connect-msg">连接成功</span>');
});
//push事件
socket.on('push', function (data) {
output(`<span class="username-msg">${data}</span>`);
console.log(data);
});
//连接释放事件
socket.on('disconnect', function () {
serverOutput('<span class="disconnect-msg">已下线! </span>');
});
//连接错误事件
socket.on("connect_error", () => {
// revert to classic upgrade
socket.io.opts.transports = ["polling", "websocket"];
});
}
//****自定义js内容,无需关心,这里引用了jquery,只做展示使用
function disconnect() {
socket.disconnect();
}
function output(message) {
const element = $("<div>" + message + "</div>");
$('#console').append(element);
}
function serverOutput(message) {
const element = $("<div>" + message + "</div>");
$('#console').append(element);
}
function send() {
socket.emit('json', {data: $("#msg").val()});
}
评论区