当前位置:   article > 正文

React+SpringBoot通过WebSocket实时统计在线人数_webscoket 统计在线人数

webscoket 统计在线人数

一、基本概念

WebSocket 是一种网络通信协议,如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)所以这里使用WebSocket 通过登录后跳转到首页,向后台WebSocket 建立长链接来达到"即使通讯",随着用户页面打开或关闭后台群发消息来实时更改页面显示的人数,当然这里目前不涉及登录后的上线下线以及帐号登录挤掉功能,如果需要可以通过发送消息来改变。

二、SpringBoot 后台实现WebSocket

pom.xml

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>

WebSocketConfig.java

  1. package com.kero99.socket;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  5. /**
  6. * 开启WebSocket支持
  7. * @author ygc
  8. */
  9. @Configuration
  10. public class WebSocketConfig {
  11. @Bean
  12. public ServerEndpointExporter serverEndpointExporter() {
  13. return new ServerEndpointExporter();
  14. }
  15. }

WebSocketController.java

  1. package com.kero99.socket;
  2. import java.io.IOException;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. import org.springframework.web.bind.annotation.RequestParam;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * WebSocket服务器端推送消息示例Controller
  9. *
  10. * @author ygc
  11. *
  12. */
  13. @RestController
  14. @RequestMapping("/scoket")
  15. public class WebSocketController {
  16. // @Autowired
  17. // private RedisOperator redisOperator;
  18. @RequestMapping(value="/sendAll", method=RequestMethod.GET)
  19. /**
  20. * 群发消息内容
  21. * @param message
  22. * @return
  23. */
  24. String sendAllMessage(@RequestParam(required=true) String message){
  25. try {
  26. WebSocketServer.BroadCastInfo(message);
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }
  30. return "ok";
  31. }
  32. @RequestMapping(value="/sendOne", method=RequestMethod.GET)
  33. /**
  34. * 指定会话ID发消息
  35. * @param message 消息内容
  36. * @param id 连接会话ID
  37. * @return
  38. */
  39. String sendOneMessage(@RequestParam(required=true) String message,@RequestParam(required=true) String id){
  40. try {
  41. WebSocketServer.SendMessage(id,message);
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. return "ok";
  46. }
  47. }

WebSocketServer.java

  1. package com.kero99.socket;
  2. import java.io.IOException;
  3. import java.util.concurrent.CopyOnWriteArraySet;
  4. import java.util.concurrent.atomic.AtomicInteger;
  5. import javax.websocket.OnClose;
  6. import javax.websocket.OnError;
  7. import javax.websocket.OnMessage;
  8. import javax.websocket.OnOpen;
  9. import javax.websocket.Session;
  10. import javax.websocket.server.ServerEndpoint;
  11. import org.slf4j.Logger;
  12. import org.slf4j.LoggerFactory;
  13. import org.springframework.stereotype.Component;
  14. /**
  15. * WebSocket服务端
  16. * @author ygc
  17. */
  18. @ServerEndpoint(value = "/websocket")
  19. @Component
  20. public class WebSocketServer {
  21. private final static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
  22. private static final AtomicInteger OnlineCount = new AtomicInteger(0);
  23. // concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
  24. private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
  25. /**
  26. * 连接建立成功调用的方法
  27. * @throws IOException
  28. */
  29. @OnOpen
  30. public void onOpen(Session session) throws IOException {
  31. SessionSet.add(session);
  32. int personCount = OnlineCount.incrementAndGet(); // 在线数加1
  33. System.out.println("有连接加入,当前连接数为:"+personCount);
  34. log.info("有连接加入,当前连接数为:{}", personCount);
  35. // SendMessage(session, "连接成功,当前连接人数为:"+personCount);
  36. // SendMessage(session,String.valueOf(personCount));
  37. BroadCastInfo(String.valueOf(OnlineCount.get()));
  38. }
  39. /**
  40. * 连接关闭调用的方法
  41. * @throws IOException
  42. */
  43. @OnClose
  44. public void onClose(Session session) throws IOException {
  45. int personCount = OnlineCount.decrementAndGet();
  46. System.out.println("有连接关闭,当前连接数为:"+personCount);
  47. log.info("有连接关闭,当前连接数为:{}", personCount);
  48. SessionSet.remove(session);
  49. }
  50. /**
  51. * 收到客户端消息后调用的方法
  52. *
  53. * @param message
  54. * 客户端发送过来的消息
  55. * @throws IOException
  56. */
  57. @OnMessage
  58. public void onMessage(String message, Session session) throws IOException {
  59. log.info("来自客户端的消息:{}",message);
  60. // System.out.println("来自客户端的消息:"+message);
  61. // SendMessage(session, "收到消息,消息内容:"+message);
  62. if(message.equals("管理平台")) {
  63. System.out.println("收到平台类型:"+message);
  64. }
  65. // if(message.equals("新增人数")) {
  66. // System.out.println("打开页面:"+message);
  67. // BroadCastInfo(String.valueOf(OnlineCount.get()+1));
  68. // }
  69. if(message.equals("关闭页面")) {
  70. System.out.println("收到关闭页面:"+message);
  71. //在线数加-1
  72. BroadCastInfo(String.valueOf(OnlineCount.get()-1));
  73. }
  74. }
  75. /**
  76. * 出现错误
  77. * @param session
  78. * @param error
  79. */
  80. @OnError
  81. public void onError(Session session, Throwable error) {
  82. log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
  83. System.out.println("发生错误:{},Session ID: "+error.getMessage()+session.getId());
  84. error.printStackTrace();
  85. }
  86. /**
  87. * 发送消息,实践表明,每次浏览器刷新,session会发生变化。
  88. * @param session
  89. * @param message
  90. */
  91. public static void SendMessage(Session session, String message) {
  92. try {
  93. session.getBasicRemote().sendText(message);
  94. // session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
  95. } catch (IOException e) {
  96. log.error("发送消息出错:{}", e.getMessage());
  97. System.out.println("发送消息出错:{}"+e.getMessage());
  98. e.printStackTrace();
  99. }
  100. }
  101. /**
  102. * 群发消息
  103. * @param message
  104. * @throws IOException
  105. */
  106. public static void BroadCastInfo(String message) throws IOException {
  107. for (Session session : SessionSet) {
  108. if(session.isOpen()){
  109. SendMessage(session, message);
  110. }
  111. }
  112. }
  113. /**
  114. * 指定Session发送消息
  115. * @param sessionId
  116. * @param message
  117. * @throws IOException
  118. */
  119. public static void SendMessage(String sessionId,String message) throws IOException {
  120. Session session = null;
  121. for (Session s : SessionSet) {
  122. if(s.getId().equals(sessionId)){
  123. session = s;
  124. break;
  125. }
  126. }
  127. if(session!=null){
  128. SendMessage(session, message);
  129. }
  130. else{
  131. log.warn("没有找到你指定ID的会话:{}",sessionId);
  132. System.out.println("没有找到你指定ID的会话:"+sessionId);
  133. }
  134. }
  135. }

三、React+Umi+Antd 实现前端 WebSocket 通讯

js 需要安装 socket.io

命令: npm  socket.io 或者 yarn add socket.io

  1. componentDidMount() {
  2. let ws = new WebSocket("ws://localhost:12935/20191108_V1.0_xdnx/websocket");
  3. if (typeof (WebSocket) == "undefined") {
  4. console.log("遗憾:您的浏览器不支持WebSocket");
  5. } else {
  6. console.log("恭喜:您的浏览器支持WebSocket");
  7. ws.onopen = (evt)=> {
  8. console.log("Connection open ...");
  9. ws.send("管理平台");
  10. ws.send("新增人数");
  11. };
  12. ws.onmessage = (evt)=> {
  13. console.log( "Received Message: " + evt.data);
  14. // alert(evt.data)
  15. //this.state.messageData 为接受数据的变量
  16. let messageData=this.state.messageData;
  17. this.setState({
  18. messageData:evt.data
  19. })
  20. // ws.close();
  21. };
  22. ws.onclose = (evt)=> {
  23. // alert(evt.data)
  24. console.log("Connection closed.");
  25. // ws.close();
  26. };
  27. ws.onerror = (evt)=> {
  28. console.log("error")
  29. };
  30. window.onbeforeunload = (event)=> {
  31. console.log("关闭WebSocket连接!");
  32. ws.send("关闭页面");
  33. event.close();
  34. }
  35. }

render html

这里用的antd的统计数值控件

  1. <div style={{
  2. background: '#ececec',
  3. padding: '10px',
  4. width:'20%',
  5. float:'left',marginTop:'20px'
  6. }}>
  7. <Row gutter={16}>
  8. <Col span={12}>
  9. <Card>
  10. <Statistic
  11. title="管理平台当前在线人数"
  12. value={this.state.messageData}
  13. precision={0}
  14. valueStyle={{ color: '#3f8600' }}
  15. suffix="人"
  16. />
  17. </Card>
  18. </Col>
  19. </Row>
  20. </div>

四、实现结果

五、关于部署

如果使用Tomcat部署war包下面这段可以注释掉,由Tomcat管理,页面路径需要改成和服务器一致的,不用加Http开头

eg: ws://localhost:12935/20191108_V1.0_xdnx/websocket

  1. //package com.kero99.socket;
  2. //
  3. //import org.springframework.context.annotation.Bean;
  4. //import org.springframework.context.annotation.Configuration;
  5. //import org.springframework.stereotype.Component;
  6. //import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  7. //
  8. ///**
  9. // * 开启WebSocket支持
  10. // * @author ygc
  11. // */
  12. //@Configuration
  13. //@Component
  14. //public class WebSocketConfig {
  15. //
  16. // @Bean
  17. // public ServerEndpointExporter serverEndpointExporter() {
  18. // return new ServerEndpointExporter();
  19. // }
  20. //
  21. //}

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/770811
推荐阅读
相关标签
  

闽ICP备14008679号