赞
踩
在web开发过程中,多数情况下是前端主动向服务端发起请求,但也有需要服务端通知前端的场景,最最典型的例子就是网页版的客服系统,聊天是需要服务端将消息传给另外一个人;本文将使用netty-socketio演示如何向前端断送消息,读者需具备基本的软件开发能力
netty-socketio 是一个开源的Socket.io 服务器端的一个Java的实现,他基于Netty框架,web可以通过websocket和socketio建立连接,双方基于事件进行互相监听
以下为pom文件当中的一些坐标
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>netty-socketio</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.17</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.67</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
@SpringBootApplication @Slf4j public class NettySocketioApplication implements CommandLineRunner { public static void main(String[] args) { SpringApplication.run(NettySocketioApplication.class, args); } @Autowired private SocketIOServer socketIOServer; @Override public void run(String... strings) { socketIOServer.start(); log.info("socket.io启动成功!"); } }
@Configuration public class NettySocketioConfig { /** * netty-socketio服务器 * 主类的socketio */ @Bean public SocketIOServer socketIOServer() { com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); config.setHostname("localhost"); config.setPort(9092);//监听的socket端口 SocketIOServer server = new SocketIOServer(config); return server; } /** * 用于扫描netty-socketio的注解,比如 @OnConnect、@OnEvent */ @Bean public SpringAnnotationScanner springAnnotationScanner() { return new SpringAnnotationScanner(socketIOServer()); } }
** * 客户端和服务端都是通过事件来交互的 * 用于监听客户端websocket的事件 * 同时也可以往客户端发送事件(客户端自己可以监听) */ @Component @Slf4j public class MessageEventHandler { @Autowired private SocketIOServer socketIoServer; /** * 线程安全的map,用于保存和客户端的回话 * * 如果是使用集群部署的情况下则不能这么使用, * 因为客户端每次命中的服务不一定是上次命中那个 * 集群解决方案:使用redis的发布订阅或者消息中间件的发布订阅 * 这样,每个服务都有listener监听着,然后可以拿到对应的客户端socketclient */ public static ConcurrentMap<String, SocketIOClient> socketIOClientMap = new ConcurrentHashMap<>(); /** * 客户端连接的时候触发 * @param client */ @OnConnect public void onConnect(SocketIOClient client) { // String mac = client.getHandshakeData().getSingleUrlParam("mac"); //存储SocketIOClient,用于发送消息 socketIOClientMap.put(mac, client); //通过client.sendEvent可以往客户端回发消息 client.sendEvent("message", "onConnect back"); log.info("客户端:" + client.getSessionId() + "已连接,mac=" + mac); } /** * 客户端关闭连接时触发 * * @param client */ @OnDisconnect public void onDisconnect(SocketIOClient client) { SocketIOClient socketIOClient = socketIOClientMap.get(client.getHandshakeData().getSingleUrlParam("mac")); if (null != socketIOClient){ socketIOClientMap.remove(socketIOClient); } log.info("客户端:" + client.getSessionId() + "断开连接"); } /** * 监听客户端事件messageevent * * @param client 客户端信息 * @param request 请求信息 * @param data 客户端发送数据 */ @OnEvent(value = "messageevent") public void onEvent(SocketIOClient client, AckRequest request, Message data) { log.info("发来消息:" + data); //回发消息 client.sendEvent("messageevent", "我是服务器都安发送的信息==" + data.getMsgContent()); //广播消息 sendBroadcast(); } /** * 监听客户端事件messageevent * * @param client 客户端信息 * @param data 客户端发送数据 */ @OnEvent(value = "messageevent2") public void messageevent2(SocketIOClient client, JSONObject data) { log.info("发来消息:" + data); //回发消息 client.sendEvent("messageevent2", "我是服务器都安发送的信息==" + data.getString("FirstName")); } /** * 广播消息 */ public void sendBroadcast() { for (SocketIOClient client : socketIOClientMap.values()) { if (client.isChannelOpen()) { client.sendEvent("Broadcast", "当前时间", System.currentTimeMillis()); } } } }
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no"> <title>websocket-java-socketio</title> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> </head> <body> <h1>Socket.io Test</h1> <div><p id="status">Waiting for input</p></div> <div><p id="message">hello world!</p></div> <button id="connect" onClick='connect()'/>Connect</button> <button id="disconnect" onClick='disconnect()'>Disconnect</button> <button id="send" onClick='send()'/>Send Message</button> </body> <script type="text/javascript"> /** * 前端js的 socket.emit("事件名","参数数据")方法,是触发后端自定义消息事件的时候使用的, * 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件 **/ var socket = io.connect("http://localhost:9092?mac=2"); var firstconnect = true; function connect() { socket.socket.reconnect(); } //监听服务器连接事件 socket.on('connect', function(){ status_update("Connected to Server"); }); //监听服务器关闭服务事件 socket.on('disconnect', function(){ status_update("Disconnected from Server"); }); //监听服务器端发送消息事件 socket.on('messageevent', function(data) { message(data) //console.log("服务器发送的消息是:"+data); }); socket.on('messageevent2', function(data) { message(data) //console.log("服务器发送的消息是:"+data); }); //断开连接 function disconnect() { socket.disconnect(); } function message(data) { document.getElementById('message').innerHTML = "Server says: " + data; } function status_update(txt){ document.getElementById('status').innerHTML = txt; } function esc(msg){ return msg.replace(/</g, '<').replace(/>/g, '>'); } //点击发送消息触发 function send() { console.log("点击了发送消息,开始向服务器发送消息") var jsonObj = {'FirstName':'xu','LastName':'Xiang'}; //socket.emit('messageevent', {msgContent: msg});//{msgContent: msg}是一个json对象,到了服务端会反序列化 //socket.emit('messageevent2', {msgContent: msg});//{msgContent: msg}是一个json对象,到了服务端会反序列化 socket.emit('messageevent2',jsonObj);//{msgContent: msg}是一个json对象,到了服务端会反序列化 }; </script> </html>
本文只是一个快速入门的案例,但已经满足了博主在项目中的需求,不足之处有一下几点:
1.客户端和服务端建立连接的对象是保存在服务本地内存中的,如果服务使用集群的方式部署,则客户端的请求不一定每次都落在同一台机器上,所以有可能会找不到对应的client对像,客户端无法收到消息;可以使用redis,mq等消息中间件来解决
2.不支持图片的等文件的发送
代码地址:https://gitee.com/supermanAndBoy/nettty-socketio-demo.git
如有侵权,请联系删除
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。