后端 + 前端简单HTML页面
1. 群发,所有人都能收到
2. 局部群发,部分人群都能收到
3. 单点推送, 指定某个人的页面
可以看到内容不多,也就是说,springboot 整合socket, 跟着我学,轻轻松松。
不多说,开始:
com.alibaba
fastjson
1.2.75
com.corundumstudio.socketio
netty-socketio
1.7.7
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
server:
port: 8089
socketio:
host: localhost
port: 8503
maxFramePayloadLength: 1048576
maxHttpContentLength: 1048576
bossCount: 1
workCount: 100
allowCustomRequests: true
upgradeTimeout: 10000
pingTimeout: 60000
pingInterval: 25000
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;
/**
* @Author: JCccc
* @Description:
* @Date: 2022/06/13 21:50
*/
@Configuration
public class MySocketConfig{
@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();
buildSocketConfig(socketConfig, config);
return new SocketIOServer(config);
}
/**
* 扫描netty-socketIo的注解( @OnConnect、@OnEvent等)
*/
@Bean
public SpringAnnotationScanner springAnnotationScanner() {
return new SpringAnnotationScanner(socketIOServer());
}
private void buildSocketConfig(SocketConfig socketConfig, com.corundumstudio.socketio.Configuration config) {
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);
}
}
/**
* @Author: JCccc
* @Date: 2022-07-23 9:05
* @Description:
*/
public class MyMessage {
private String type;
private String content;
private String from;
private String to;
private String channel;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getChannel() {
return channel;
}
public void setChannel(String channel) {
this.channel = channel;
}
}
MySocketHandler.java
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.socket.mysocket.util.SocketUtil;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @Author: JCccc
* @Description:
* @Date: 2022/6/23 21:21
*/
@Component
public class MySocketHandler {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private SocketIOServer socketIoServer;
@PostConstruct
private void start(){
try {
socketIoServer.start();
}catch (Exception e){
e.printStackTrace();
}
}
@PreDestroy
private void destroy(){
try {
socketIoServer.stop();
}catch (Exception e){
e.printStackTrace();
}
}
@OnConnect
public void connect(SocketIOClient client) {
String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
SocketUtil.connectMap.put(userFlag, client);
log.info("客户端userFlag: "+ userFlag+ "已连接");
}
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
String userFlag = client.getHandshakeData().getSingleUrlParam("userFlag");
log.info("客户端userFlag:" + userFlag + "断开连接");
SocketUtil.connectMap.remove(userFlag, client);
}
}
SocketUtil.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @Author: JCccc
* @Description:
* @Date: 2022/6/23 21:28
*/
@Component
public class SocketUtil {
private final Logger log = LoggerFactory.getLogger(this.getClass());
//暂且把用户&客户端信息存在缓存
public static ConcurrentMap connectMap = new ConcurrentHashMap<>();
@OnEvent(value = "CHANNEL_SYSTEM")
public void systemDataListener(String receiveMsg) {
if (!StringUtils.hasLength(receiveMsg)){
return;
}
JSONObject msgObject = (JSONObject) JSON.parse(receiveMsg);
String userFlag = String.valueOf(msgObject.get("from"));
String content = String.valueOf(msgObject.get("content"));
log.info("收到用户 : {} 推送到系统频道的一条消息 :{}",userFlag,content );
}
public void sendToAll(Map msg,String sendChannel) {
if (connectMap.isEmpty()){
return;
}
//给在这个频道的每个客户端发消息
for (Map.Entry entry : connectMap.entrySet()) {
entry.getValue().sendEvent(sendChannel, msg);
}
}
public void sendToOne(String userFlag, Map msg,String sendChannel) {
//拿出某个客户端信息
SocketIOClient socketClient = getSocketClient(userFlag);
if (Objects.nonNull(socketClient) ){
//单独给他发消息
socketClient.sendEvent(sendChannel,msg);
}
}
/**
* 识别出客户端
* @param userFlag
* @return
*/
public SocketIOClient getSocketClient(String userFlag){
SocketIOClient client = null;
if (StringUtils.hasLength(userFlag) && !connectMap.isEmpty()){
for (String key : connectMap.keySet()) {
if (userFlag.equals(key)){
client = connectMap.get(key);
}
}
}
return client;
}
}
TestController.java
import com.socket.mysocket.dto.MyMessage;
import com.socket.mysocket.util.SocketUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: JCccc
* @Description:
* @Date: 2022/06/13 21:50
*/
@RestController
public class TestController {
public final static String SEND_TYPE_ALL = "ALL";
public final static String SEND_TYPE_ALONE = "ALONE";
@Autowired
SocketUtil socketUtil;
@PostMapping("/testSendMsg")
public String testSendMsg(@RequestBody MyMessage myMessage){
Map map = new HashMap<>();
map.put("msg",myMessage.getContent());
//群发
if (SEND_TYPE_ALL.equals(myMessage.getType())){
socketUtil.sendToAll( map,myMessage.getChannel());
return "success";
}
//指定单人
if (SEND_TYPE_ALONE.equals(myMessage.getType())){
socketUtil.sendToOne(myMessage.getTo(), map, myMessage.getChannel());
return "success";
}
return "fail";
}
}
好了,7步了。一切已经就绪了。
接下来搞点前端HTML页面, 玩一玩看看效果:
第一个页面:
TestClientStudentJC.html
我要连SOCKET
给系统推消息
第二个页面,跟第一个基本一样,改一下用户唯一标识:
TestClientStudentPU.html
我要连SOCKET
给系统推消息
OK,把项目跑起来,开始玩。
直接访问客户端页面 模拟学生 JC连接socket:
http://127.0.0.1:8089/TestClientStudentJC.html
先试试客户端给系统推消息先:
可以看到服务端成功收到消息:
ps: 其实前端给服务端推消息,其实调用接口就可以。
OK,进入核心应用场景1:
{
"type": "ALL",
"content":"你们好,这是一条广播消息,全部人都能收到",
"channel":"CHANNEL_SYSTEM"
}
其实也就是通过HTML 客户端监听主题做区分就好:
直接拉人口,升3 :
模拟2个学生,1个老师都连接上了socket
然后我们模拟推送一下消息到指定的老师频道:
{
"type": "ALL",
"content":"给老师们推一条消息!!!",
"channel":"CHANNEL_TEACHER"
}
模拟 学生 PU 给 学生JC 推消息:
可以看到在学生频道的JC正常收到了PU的消息:
好了,该篇就到这吧。