springboot + websocket 实现聊天室

1.前言

实现聊天室有很多种方式 netty, websocket等,我们这里直接使用websocket技术,websocket是一种服务器向客户端发送信息的技术,而不是传统的servlet客户端发送请求,然后服务器给出响应.

现在比较流行的框架是springboot,而且spring官方也支持websocket,这里借鉴了spring的官网文档,不排除以后技术会发生变化.

2.实现

2.1依赖

    implementation 'org.springframework.boot:spring-boot-starter-websocket'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation 'org.webjars:webjars-locator-core'
    implementation 'org.webjars:sockjs-client:1.0.2'
    implementation 'org.webjars:stomp-websocket:2.3.3'
    implementation 'org.webjars:bootstrap:3.3.7'
    implementation 'org.webjars:jquery:3.1.1-1'

2.2配置文件

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/gs-guide-websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic","/user");
        registry.setApplicationDestinationPrefixes("/app");
    }

}
setApplicationDestinationPrefixes 定义了websocket应用的访问前缀,
addEndpoint  定义了socketjs的连接,

enableSimpleBroker 定义了启用的广播路径,如果这里不加上路径的话,websocket无法广播,也就是不法向客户端发送消息


2.3消息类

 

public class HelloMessage {
    private String name;

    public HelloMessage() {
        super();
    }

    public HelloMessage(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}


public class Greeting {
    private String content;

    public Greeting() {
        super();
        // TODO Auto-generated constructor stub
    }

    public Greeting(String content) {
        super();
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
    
}

没什么好说的,信息载体类

 

2.4controller

@Controller
public class GreetingController {
    
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    
    @MessageMapping("/hello")
    //@SendTo
    //@SendToUser
    public Greeting greeting(HelloMessage message) throws InterruptedException {
        Greeting result = new Greeting("Hello,"+HtmlUtils.htmlEscape(message.getName()));
        TimeUnit.SECONDS.sleep(1L);
        simpMessagingTemplate.convertAndSendToUser("bob", "/queue/position-updates", result);
        return new Greeting("Hello,"+HtmlUtils.htmlEscape(message.getName()));
    }

}

我们可以使用 SendTo 定义返回路径,也可以使用 SendToUser 定义返回的用户,这些都是使用注解定义的,如果想要更灵活的可以使用 SimpMessagingTemplate 这个类来广播消息.

 

2.5 前端实现

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <noscript>
    <h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
      enabled. Please enable
      Javascript and reload this page!</h2>
  </noscript>
  <div id="main-content" class="container">
    <div class="row">
      <div class="col-md-6">
        <form class="form-inline">
          <div class="form-group">
            <label for="connect">WebSocket connection:</label>
            <button id="connect" class="btn btn-default" type="submit">Connect</button>
            <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
            </button>
          </div>
        </form>
      </div>
      <div class="col-md-6">
        <form class="form-inline">
          <div class="form-group">
            <label for="name">What is your name?</label>
            <input type="text" id="name" class="form-control" placeholder="Your name here...">
          </div>
          <button id="send" class="btn btn-default" type="submit">Send</button>
        </form>
      </div>
    </div>
    <div class="row">
      <div class="col-md-12">
        <table id="conversation" class="table table-striped">
          <thead>
            <tr>
              <th>Greetings</th>
            </tr>
          </thead>
          <tbody id="greetings">
          </tbody>
        </table>
      </div>
    </div>
  </div>




  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
  <script>
    var stompClient = null;

    function setConnected(connected) {
      $("#connect").prop("disabled", connected);
      $("#disconnect").prop("disabled", !connected);
      if (connected) {
        $("#conversation").show();
      }
      else {
        $("#conversation").hide();
      }
      $("#greetings").html("");
    }

    function connect() {
      var socket = new SockJS('/gs-guide-websocket');
      stompClient = Stomp.over(socket);
      stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/user/bob/queue/position-updates', function (greeting) {
        console.log(greeting)
          showGreeting(JSON.parse(greeting.body).content);
        });
      });
    }

    function disconnect() {
      if (stompClient !== null) {
        stompClient.disconnect();
      }
      setConnected(false);
      console.log("Disconnected");
    }

    function sendName() {
      stompClient.send("/app/hello", {}, JSON.stringify({ 'name': $("#name").val() }));
    }

    function showGreeting(message) {
      $("#greetings").append("<tr><td>" + message + "</td></tr>");
    }

    $(function () {
      $("form").on('submit', function (e) {
        e.preventDefault();
      });
      $("#connect").click(function () { connect(); });
      $("#disconnect").click(function () { disconnect(); });
      $("#send").click(function () { sendName(); });
    });
  </script>
</body>

</html>

 

自己看代码吧 就不多解释了,剩下就是根据业务需要改改就可以.

 

 

上一篇:15. 三数之和(双指针)


下一篇:H5中为什么要有websocket