一.快速入门
1.导依赖
<!-- socket.io依赖-->
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>io.socket</groupId>
<artifactId>socket.io-client</artifactId>
<version>1.0.0</version>
</dependency>
2.在配置文件中做一些配置
这个配置写在服务端,客户端不用写,主要是一些socket.io的配置信息。
#socket.io配置
socketio.host=127.0.0.1(别写localhost,写服务器的ip)
socketio.port=9999
# 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器
socketio.maxFramePayloadLength=1048576
# 设置http交互最大内容长度
socketio.maxHttpContentLength=1048576
# socket连接数大小(如只监听一个端口boss线程组为1即可)
socketio.bossCount=1
socketio.workCount=100
socketio.allowCustomRequests=true
# 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间
socketio.upgradeTimeout=1000000
# Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件
socketio.pingTimeout=6000000
# Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔
socketio.pingInterval=25000
3.用上面配置文件中的信息创建Bean对象(服务端)
package com.guguo.config;
import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.SpringAnnotationScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SocketIOConfig {
@Value("${socketio.host}")
private String host;
@Value("${socketio.port}")
private Integer port;
@Value("${socketio.bossCount}")
private int bossCount;
@Value("${socketio.workCount}")
private int workCount;
@Value("${socketio.allowCustomRequests}")
private boolean allowCustomRequests;
@Value("${socketio.upgradeTimeout}")
private int upgradeTimeout;
@Value("${socketio.pingTimeout}")
private int pingTimeout;
@Value("${socketio.pingInterval}")
private int pingInterval;
@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(host);
config.setPort(port);
config.setBossThreads(bossCount);
config.setWorkerThreads(workCount);
config.setAllowCustomRequests(allowCustomRequests);
config.setUpgradeTimeout(upgradeTimeout);
config.setPingTimeout(pingTimeout);
config.setPingInterval(pingInterval);
return new SocketIOServer(config);
}
//这个对象是用来扫描socketio的注解,比如 @OnConnect、@OnEvent
@Bean
public SpringAnnotationScanner springAnnotationScanner() {
return new SpringAnnotationScanner(socketIOServer());
}
}
4.写Handler,里面包含了处理基本事件的方法(服务端)
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Slf4j
@Component
public class SocketIOHandler {
@Autowired
private SocketIOServer socketIoServer;//服务端对象
//Spring IoC容器创建之后,在加载SocketIOService Bean之后启动
//服务端启动
@PostConstruct
private void autoStartup() throws Exception {
try {
socketIoServer.start();
}catch (Exception ex){
ex.printStackTrace();
log.error("SocketIOServer启动失败");
}
}
//Spring IoC容器在销毁SocketIOService Bean之前关闭,避免重启项目服务端口占用问题
//服务端关闭
@PreDestroy
private void autoStop() throws Exception {
socketIoServer.stop();
}
//客户端连接的时候触发
@OnConnect
public void onConnect(SocketIOClient client) {
String username = client.getHandshakeData().getSingleUrlParam("userName");
SocketIOService.clientMap.put(username,client);
//客户端:/127.0.0.1:64540 sessionId:9acac9ab-a000-43a7-bf97-31fac791e4f4 username: 张三已连接
log.info("客户端:" + client.getRemoteAddress() + " sessionId:" + client.getSessionId() +" username: "+ username+ "已连接");
}
//客户端关闭连接时触发
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
//客户端:9acac9ab-a000-43a7-bf97-31fac791e4f4断开连接
log.info("客户端:" + client.getSessionId() + "断开连接");
}
}
5.客户端测试,Controller层代码:
import com.itheima.boot.service.SocketIOClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
public class SocketIOClientController {
@Autowired
private SocketIOClientService socketIOClientService;
//测试和服务端的连接
@GetMapping("/testConnect")
public void testConnect() throws IOException {
socketIOClientService.sendMessage("张三");
}
}
6.客户端测试,Service层代码:
package com.itheima.boot.service;
import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SocketIOClientService{
public void sendMessage(String name) {
// 服务端socket.io连接通信地址
String url = "http://127.0.0.1:9999?userName="+name;
System.out.println("lalalala");
try {
IO.Options options = new IO.Options();
options.transports = new String[]{"websocket"};
//失败后重试次数
options.reconnectionAttempts = 2;
// 失败重连的时间间隔
options.reconnectionDelay = 1000;
// 连接超时时间(ms)
options.timeout = 500;
final Socket socket = IO.socket(url, options);
socket.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.结果
二.服务端发数据,客户端接收
1.服务端Controller代码
package com.guguo.controller;
import com.guguo.service.SocketIOService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SocketIOController {
@Autowired
private SocketIOService socketIOService;
@GetMapping("/sendMessage")
public void sendMessage(String message){
socketIOService.sendMessageToAllUsers(message);
}
}
2.服务端Service代码
import com.corundumstudio.socketio.SocketIOClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
public class SocketIOService {
//使用ConcurrentMap来存储客户端
public static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();
//给每个事件进行定义,用于客户端和服务端之间的通信
private static final String TEST_EVENT = "test_event";
//给容器内所有的客户端发送信息
public void sendMessageToAllUsers(String message){
if(clientMap.isEmpty()){
return;
}
for(String key:clientMap.keySet()) {
SocketIOClient socketIOClient = clientMap.get(key);
socketIOClient.sendEvent(TEST_EVENT,message);
}
}
}
3.客户端接收代码
package com.itheima.boot.service;
import io.socket.client.IO;
import io.socket.client.Socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class SocketIOClientService{
public void sendMessage(String name) {
// 服务端socket.io连接通信地址
String url = "http://127.0.0.1:9999?userName="+name;
try {
IO.Options options = new IO.Options();
options.transports = new String[]{"websocket"};
//失败后重试次数
options.reconnectionAttempts = 2;
// 失败重连的时间间隔
options.reconnectionDelay = 1000;
// 连接超时时间(ms)
options.timeout = 500;
final Socket socket = IO.socket(url, options);
//客户端监听某个事件,这个是系统定义的事件(链接事件)
socket.on(Socket.EVENT_CONNECT, message -> System.out.println("连接成功"));
// 自定义事件`test_event` -> 接收服务端成功连接消息
socket.on("test_event", objects -> System.out.println(objects[0].toString()));
socket.connect();
while (true) {
Thread.sleep(4000);
// 向服务端发送消息
socket.emit("push_data_event", "发送数据 " + "oejfjefwfwefw");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.注意问题和结果展示
服务端的逻辑是,向所有已经连接的客户端发送数据。所以,客户端要先向服务端发送请求连接,连接之后服务端将该客户端存到本地,然后再执行发送数据的逻辑。
客户端控制台:
服务端控制台: