Spring-Boot快速集成netty-socketio(socket服务实现,支持认证)
Spring-Boot快速集成netty-socketio(socket服务实现,支持认证)
netty-socketio是一个开源的Socket.io服务器端的一个java的实现,它基于Netty框架,可用于服务端推送消息给客户端。
说到服务端推送技术,一般会涉及WebSocket,WebSocket是HTML5最新提出的规范,虽然主流浏览器都已经支持,但仍然可能有不兼容的情况,为了兼容所有浏览器,给程序员提供一致的编程体验,SocketIO将WebSocket、AJAX和其它的通信方式全部封装成了统一的通信接口,也就是说,使用SocketIO时不用担心兼容问题,底层会自动选用最佳的通信
基于Tomcat的webSocket的并发量很低,所以使用netty框架的socket性能更好,反应更快,兼容更好,而且自带认证
netty-socketio 框架事件流程
socket服务配置
yml配置
#socket服务配置 club.dlblog.socketio: host: localhost #IP地址 port: 9000 #端口号
socket服务配置类
/**
* socket服务配置
* @author machenike
*/ @Configuration public class NettySocketConfig { @Value("${club.dlblog.socketio.host}") private String host; @Value("${club.dlblog.socketio.port}") private Integer port; /**
* netty-socketio服务器
* @return
**/ @Bean public SocketIOServer socketIOServer() { com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); //设置host config.setHostname(host); //设置端口 config.setPort(port); SocketIOServer server = new SocketIOServer(config); //启动socket服务 server.start(); return server; } /**
*用于扫描netty-socketio的注解,比如 @OnConnect、@OnEvent
*
**/ @Bean public SpringAnnotationScanner springAnnotationScanner() { return new SpringAnnotationScanner(socketIOServer()); } /**
* 注入socket处理拦截器
* @return
*/ @Bean public NettySocketHandler nettySocketHandler(){ return new NettySocketHandler(); } }
认证监听
认证用服务定义
/**
* 默认认证服务实现
* @author machenike
*/ public class DefaultSocketAuthServiceImpl implements SocketAuthService { /**
* 日志
*/ private final static Logger logger = LoggerFactory.getLogger(DefaultSocketAuthServiceImpl.class); private final static String QUERY_CLIENT_ID = "clientId"; static Map<String, SocketIOClient> socketMap; static{ socketMap = NettySocketHandler.clientMap; } @Override public boolean auth(HandshakeData handshakeData) { String clientId = handshakeData.getSingleUrlParam(QUERY_CLIENT_ID); if(clientId!=null){ //若客户端存在 if(socketMap.get(clientId)!=null){ logger.debug("current socket clientId - "+clientId+" is repeated"); //认证失败 return false; } logger.debug("socket client auth success [clientId="+clientId+"]"); return true; } logger.debug("socket client auth failed [clientId="+clientId+"]"); return false; } }
认证监听器定义,当isAuthorized方法返回false时,认证失败,连接失败返回401
/**
* 认证监听器
* @author machenike
*/ public class SocketAuthListener implements AuthorizationListener { private SocketAuthService authService; @Override public boolean isAuthorized(HandshakeData handshakeData) { return authService.auth(handshakeData); } public SocketAuthListener(SocketAuthService authService) { this.authService = authService; } public SocketAuthListener() { this.authService = new DefaultSocketAuthServiceImpl(); } }
注入认证监听
在上述的config类中增加监听器配置
/**
* netty-socketio服务器
* @return
**/ @Bean public SocketIOServer socketIOServer() { com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); //设置host config.setHostname(host); //设置端口 config.setPort(port); //初始化认证监听器 AuthorizationListener SocketAuthListener = new SocketAuthListener(); //设置认证监听器 config.setAuthorizationListener(SocketAuthListener); SocketIOServer server = new SocketIOServer(config); //启动socket服务 server.start(); return server; }
事件监听
当前连接成功时触发onConnect事件,同时将客户端实例保存到线程安全的ConcurrentHashMap中。
/**
* socket处理拦截器
* @author machenike
*/ public class NettySocketHandler implements CommandLineRunner { /**
* 日志
*/ private final static Logger logger = LoggerFactory.getLogger(NettySocketHandler.class); /**
* 客户端保存用Map
*/ public static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>(); /**
* 连接数
*/ public static AtomicInteger onlineCount = new AtomicInteger(0); private final static String QUERY_CLIENT_ID = "clientId"; /**
* 客户端连上socket服务器时执行此事件
* @param client
*/ @OnConnect public void onConnect(SocketIOClient client) { String clientId = client.getHandshakeData().getSingleUrlParam(QUERY_CLIENT_ID); logger.debug("onConnect: [clientId="+clientId+"]"); if(clientId!=null) { clientMap.put(clientId, client); onlineCount.addAndGet(1); logger.debug("connect success: [clientId="+clientId+",onlineCount="+onlineCount.get()+"]"); } } /**
* 客户端断开socket服务器时执行此事件
* @param client
*/ @OnDisconnect public void onDisconnect(SocketIOClient client) { String clientId = client.getHandshakeData().getSingleUrlParam(QUERY_CLIENT_ID); if (clientId != null) { clientMap.remove(clientId); client.disconnect(); onlineCount.addAndGet(-1); logger.debug("disconnect success: [clientId="+clientId+",onlineCount="+onlineCount.get()+"]"); } } /**
*
* @param client
*/ @OnEvent( value = "message") public void onMessage(SocketIOClient client, AckRequest request, Object data) { String clientId = client.getHandshakeData().getSingleUrlParam(QUERY_CLIENT_ID); logger.debug("onMessage: [clientId="+clientId+",data="+data+"]"); //request.sendAckData("message is revived"); NettySocketUtil.sendNotice("test message"); client.sendEvent("ack",1); } @Override public void run(String... args) throws Exception { logger.debug("socketHandler start-------------------------------"); } }
到此SocketIO服务端代码就结束了
测试
在线演示地址
https://www.dlblog.club/file/20200808/IGMaDsur.html
消息推送
因为我将存储客户端的Map设置成public static,全局都可以取得,线程安全多线程可用
/**
* socket发送消息用工具类
* @author machenike
*/ public class NettySocketUtil { /**
* 保存client资源Map
*/ static Map<String, SocketIOClient> socketMap; static{ //client资源Map赋值 socketMap = NettySocketHandler.clientMap; } /**
* 发送消息 指定客户端 指定event
* @param clientId
* @param event
* @param message
*/ public static void sendMessage(String clientId,String event,Object message){ socketMap = NettySocketHandler.clientMap; socketMap.get(clientId).sendEvent(event,message); } /**
* 发送消息 指定客户端
* @param clientId
* @param message
*/ public static void sendMessage(String clientId,Object message){ socketMap = NettySocketHandler.clientMap; socketMap.get(clientId).sendEvent("message",message); } /**
* 发送消息 全部客户端
* @param message
*/ public static void sendNotice(Object message){ Set<String> clientIdSet = socketMap.keySet(); for(String clientId:clientIdSet){ socketMap.get(clientId).sendEvent("message",message); } } /**
* 发送消息 指定event 全部客户端
* @param message
*/ public static void sendNotice(Object message,String event){ Set<String> clientIdSet = socketMap.keySet(); for(String clientId:clientIdSet){ socketMap.get(clientId).sendEvent(event,message); } } }
demo地址
https://github.com/DavidLei08/BlogNettySocket.git
本文地址:https://blog.csdn.net/qq_42271561/article/details/107892447
上一篇: Spingboot整合thymeleaf
下一篇: JVM相关面试题总结