<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
後端 + 前端簡單HTML頁面
1. 群發,所有人都能收到
2. 區域性群發,部分人群都能收到
3. 單點推播, 指定某個人的頁面
慣例,先看看本次實戰範例專案結構:
可以看到內容不多,也就是說,springboot 整合socket, 跟著我學,輕輕鬆鬆。
古有曹植七步成詩,如今,咱們也是 7步學會整合socket!
不多說,開始:
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency> <dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
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<String, SocketIOClient> 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<String, Object> msg,String sendChannel) { if (connectMap.isEmpty()){ return; } //給在這個頻道的每個使用者端發訊息 for (Map.Entry<String, SocketIOClient> entry : connectMap.entrySet()) { entry.getValue().sendEvent(sendChannel, msg); } } public void sendToOne(String userFlag, Map<String, Object> 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<String, Object> 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
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>我要連SOCKET</title> <base> <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <style> body { padding: 20px; } #console { height: 450px; overflow: auto; } .msg-color { color: green; } </style> </head> <body> <div id="console" class="well"></div> <div id="conversationDiv"> <labal>給系統推訊息</labal> <input type="text" id="content"/> <button id="btnSendToSystem" onclick="sendSys();">傳送</button> </div> </body> <script type="text/javascript"> var socket; connect(); function connect() { var userFlag = 'user_JC'; var opts = { query: 'userFlag=' + userFlag }; socket = io.connect('http://localhost:8503', opts); socket.on('connect', function () { console.log("連線成功"); output('當前使用者是:' + userFlag ); output('<span class="msg-color">連線成功了。</span>'); }); socket.on('disconnect', function () { output('<span class="msg-color">下線了。 </span>'); }); socket.on('CHANNEL_STUDENT', function (data) { let msg= JSON.stringify(data) output('收到學生頻道訊息了:' + msg ); console.log(data); }); socket.on('CHANNEL_SYSTEM', function (data) { let msg= JSON.stringify(data) output('收到系統全域性訊息了:' + msg ); console.log(data); }); } function sendSys() { console.log('傳送訊息給伺服器端'); var content = document.getElementById('content').value; socket.emit('CHANNEL_SYSTEM',JSON.stringify({ 'content': content, 'from': 'user_JC' })); } function output(message) { var element = $("<div>" + message + "</div>"); $('#console').prepend(element); } </script> </html>
程式碼簡析:
第二個頁面,跟第一個基本一樣,改一下使用者唯一標識:
TestClientStudentPU.html
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>我要連SOCKET</title> <base> <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <style> body { padding: 20px; } #console { height: 450px; overflow: auto; } .msg-color { color: green; } </style> </head> <body> <div id="console" class="well"></div> <div id="conversationDiv"> <labal>給系統推訊息</labal> <input type="text" id="content"/> <button id="btnSendToSystem" onclick="sendSys();">傳送</button> </div> </body> <script type="text/javascript"> var socket; connect(); function connect() { var userFlag = 'user_PU'; var opts = { query: 'userFlag=' + userFlag }; socket = io.connect('http://localhost:8503', opts); socket.on('connect', function () { console.log("連線成功"); output('當前使用者是:' + userFlag ); output('<span class="msg-color">連線成功了。</span>'); }); socket.on('disconnect', function () { output('<span class="msg-color">下線了。 </span>'); }); socket.on('CHANNEL_STUDENT', function (data) { let msg= JSON.stringify(data) output('收到學生頻道訊息了:' + msg ); console.log(data); }); socket.on('CHANNEL_SYSTEM', function (data) { let msg= JSON.stringify(data) output('收到系統全域性訊息了:' + msg ); console.log(data); }); } function sendSys() { console.log('傳送訊息給伺服器端'); var content = document.getElementById('content').value; socket.emit('CHANNEL_SYSTEM',JSON.stringify({ 'content': content, 'from': 'user_PU' })); } function output(message) { var element = $("<div>" + message + "</div>"); $('#console').prepend(element); } </script> </html>
OK,把專案跑起來,開始玩。
直接存取使用者端頁面 模擬學生 JC連線socket:
http://127.0.0.1:8089/TestClientStudentJC.html
可以看到伺服器端有監測到:
這裡監測的:
先試試使用者端給系統推訊息先:
可以看到伺服器端成功收到訊息:
這種方式,其實是因為服務監聽了相關的頻道:
前端使用JS推到這個系統頻道:
ps: 其實前端給伺服器端推訊息,其實呼叫介面就可以。
OK,進入核心應用場景1:
系統給連上的使用者端都推播訊息
{ "type": "ALL", "content":"你們好,這是一條廣播訊息,全部人都能收到", "channel":"CHANNEL_SYSTEM" }
看看效果
其實也就是通過HTML 使用者端監聽主題做區分就好:
直接拉人口,升3 :
模擬2個學生,1個老師都連線上了socket
當然,老師監聽的是 老師頻道:
然後我們模擬推播一下訊息到指定的老師頻道:
{ "type": "ALL", "content":"給老師們推一條訊息!!!", "channel":"CHANNEL_TEACHER" }
模擬 學生 PU 給 學生JC 推訊息:
可以看到在學生頻道的JC正常收到了PU的訊息:
到此這篇關於Springboot整合Socket實現單點傳送,廣播群發,1對1,1對多實戰的文章就介紹到這了,更多相關Springboot Socket實戰內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45