netty-socketio是一個開源的Socket.io服務器端的一個java的實現, 它基於Netty框架。
javascript
一、參考資料html
(1)netty-socketio項目github地址: https://github.com/mrniko/netty-socketiojava
(2)netty-socketio-demo:https://github.com/mrniko/netty-socketio-demo
jquery
(3)spring boot 下集成netty socket.io:https://blog.csdn.net/whyistao/article/details/79240445linux
二、helloworld案例:實現訂閱/廣播功能git
本demo保存地址:https://github.com/wenbinouyang/oy_javagithub
demo使用 springboot 2.1.4.RELEASE,項目整體結構:web
application.propertiesspring
logging.file=/home/wwwlogs/nettysocketio007/log.log
pom.xmlshell
<?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 http://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.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.oy</groupId> <artifactId>nettysocketio007</artifactId> <version>0.0.1</version> <name>nettysocketio007</name> <description>nettysocketio007 for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
springboot的啓動類:
package com.oy; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; import com.corundumstudio.socketio.Configuration; import com.corundumstudio.socketio.SocketIOServer; import com.corundumstudio.socketio.annotation.SpringAnnotationScanner; @SpringBootApplication @Order(1) public class Nettysocketio007Application implements CommandLineRunner { private SocketIOServer server; public static void main(String[] args) { SpringApplication.run(Nettysocketio007Application.class, args); } @Bean public SocketIOServer socketIOServer() { Configuration config = new Configuration(); config.setHostname("localhost"); config.setPort(4001); this.server = new SocketIOServer(config); return server; } @Bean public SpringAnnotationScanner springAnnotationScanner(SocketIOServer socketServer) { return new SpringAnnotationScanner(socketServer); } @Override public void run(String... args) throws Exception { server.start(); UtilFunctions.log.info("socket.io run success!"); // 向"channel_1" push數據 Service.send(args); } }
MessageEventHandler類:
package com.oy; import java.util.Set; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; 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; @Component public class MessageEventHandler { public static SocketIOServer socketIoServer; @Autowired public MessageEventHandler(SocketIOServer server) { MessageEventHandler.socketIoServer = server; } @OnConnect public void onConnect(SocketIOClient client) { UUID socketSessionId = client.getSessionId(); String ip = client.getRemoteAddress().toString(); UtilFunctions.log.info("client connect, socketSessionId:{}, ip:{}", socketSessionId, ip); } @OnEvent("sub") public void sub(SocketIOClient client, AckRequest request, String channel) { UUID socketSessionId = client.getSessionId(); String ip = client.getRemoteAddress().toString(); client.joinRoom(channel); UtilFunctions.log.info("client sub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip); Set<String> rooms = client.getAllRooms(); for (String room : rooms) { UtilFunctions.log.info("after client connect, room:{}", room); } // 客戶端一訂閱,就立刻push一次 sendAllEvent(Service.getMsg()); } // @OnEvent("unsub") // public void unsub(SocketIOClient client, AckRequest request, String channel) { // UUID socketSessionId = client.getSessionId(); // String ip = client.getRemoteAddress().toString(); // client.leaveRoom(channel); // UtilFunctions.log.info("client unsub, channel:{}, socketSessionId:{}, ip:{}", channel, socketSessionId, ip); // } @OnDisconnect public void onDisconnect(SocketIOClient client) { UUID socketSessionId = client.getSessionId(); String ip = client.getRemoteAddress().toString(); UtilFunctions.log.info("client disconnect, socketSessionId:{}, ip:{}", socketSessionId, ip); Set<String> rooms = client.getAllRooms(); for (String room : rooms) { UtilFunctions.log.info("after client disconnect, room:{}", room); } } // broadcast to channel "channel_1" public static void sendAllEvent(String data) { socketIoServer.getRoomOperations("channel_1").sendEvent("channel_1", data); } }
Service類:
package com.oy; import com.alibaba.fastjson.JSONObject; public class Service { private static String msg; // 向"channel_1" push數據 public static void send(String[] args) throws Exception { int price = 0; if (args != null && args.length > 0) { try { price = Integer.parseInt(args[0]); } catch (Exception e) { UtilFunctions.log.info("args[0]不能轉換爲int"); new Exception(e); } } for (int i = 0; i < 1000; i++) { JSONObject data = new JSONObject(); data.put("current_price", price++); Service.msg = data.toJSONString(); // 把每次push的數據保存起來 MessageEventHandler.sendAllEvent(data.toJSONString()); Thread.sleep(1000 * 5); } } public static String getMsg() { return msg; } public static void setMsg(String msg) { Service.msg = msg; } }
UtilFunctions類:
package com.oy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class UtilFunctions { public static Logger log = LoggerFactory.getLogger("nettyws007"); }
三、客戶端html頁面
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script src="http://code.jquery.com/jquery-1.12.4.min.js"></script> <script type="text/javascript" src="socket.io.js"></script> <style> body { padding:20px; } #console { overflow: auto; } .username-msg {color:orange;} .connect-msg {color:green;} .disconnect-msg {color:red;} .send-msg {color:#888} </style> </head> <body> <div id="console" class="well"> </div><br/><br/> current_price: <span id="current_price"></span><br/> </body> <script type="text/javascript"> var socket = io.connect('http://localhost:4001', { transports:['websocket'] }); socket.on('connect', function() { console.log("msg頁面鏈接成功!"); socket.emit('sub', "channel_1"); output('<span class="connect-msg">Client has connected to the server!</span>'); output('<span class="connect-msg">Client send {"event": "sub", "channel": "channel_1"}</span>'); }); socket.on('disconnect', function() { output('<span class="disconnect-msg">The client has disconnected!</span>'); }); socket.on('channel_1', function(data) { var jsonObj = eval("(" + data + ")"); console.log("收到cfd_md的消息:" + data); $("#current_price").html(jsonObj.current_price); }); function output(message) { var currentTime = "<span class='time'>" + NowTime() + "</span>"; var element = $("<div>" + currentTime + " " + message + "</div>"); $('#console').prepend(element); } // 獲取當前時間 function NowTime() { var time=new Date(); var year=time.getFullYear();//獲取年 var month=time.getMonth()+1;//或者月 var day=time.getDate();//或者天 var hour=time.getHours();//獲取小時 var minu =time.getMinutes();//獲取分鐘 var second=time.getSeconds();//或者秒 var data=year+"-"; if(month<10){ data+="0"; } data+=month+"-"; if(day<10){ data+="0" } data+=day+" "; if(hour<10){ data+="0" } data+=hour+":"; if(minu<10){ data+="0" } data+=minu+":"; if(second<10){ data+="0" } data+=second; return data; } </script> </html>
四、測試結果
4.一、控制檯結果:
從控制檯結果能夠看出,有一個默認爲" "的room;當客戶端關閉時,將會從全部的room中離開。
4.二、下載一個socket.io.js,與下面的html頁面保存在一個文件夾下。雙擊html測試頁面,F12打開開發者工具。瀏覽器結果:
五、將項目打成可執行jar包
選中項目,右鍵Run As, Maven build...,運行clean package命令。若是報錯,多是src/test/java裏面的代碼的問題,刪除便可。或者可使用clean package -DskipTests跳過測試過程,並將項目打包。
選中項目,右鍵Properties,
能夠將打好的jar重命名爲a.jar(名字隨意)。shift+右鍵,在此處打開Powershell,輸入命令java -jar a.jar便可啓動socket服務。
六、將jar上傳到linux服務器,而且註冊一個服務,專門用來開啓jar提供的socket服務
(1)將a.jar重命名爲nettysocketio007.jar,上傳到linux的/home/socketio/目錄下。
(2)進入到/etc/systemd/system
(3)vim nettysocketio007.service,輸入以下內容:
[Unit] Description=nettysocketio007 Service [Service] Type=simple ExecStart=/usr/bin/java -jar -server /home/socketio/nettysocketio007.jar StandardOutput=null WorkingDirectory=/home/socketio Restart=on-failure [Install] WantedBy=multi-user.target Alias=nettysocketio007.service
(4)開啓服務:systemctl start nettysocketio007,至關於執行了命令:/usr/bin/java -jar -server /home/socketio/nettysocketio007.jar。
注意:
a、/usr/bin/java是我服務器上java命令的path,若是將JAVA_HOME/bin配置進了path,直接java -jar -server /home/socketio/nettysocketio007.jar便可。
b、WorkingDirectory=/home/socketio指定jar包的位置。
(5)查看服務:systemctl status nettysocketio007
(6)中止服務:systemctl stop nettysocketio007。中止服務後最好等一小會再開啓服務,不然可能報Failed to execute CommandLineRunner。