代码编织梦想

前言

最近由于一些原因很久没写文章了,今天给大家分享一个Spring方式使用ws长连接实现简单聊天室功能

什么是websocket?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

为什么有了HTTP协议还要WebSocket

HTTP协议采用的是客户端(浏览器)轮询的方式,即客户端发送请求,服务端做出响应,为了获取最新的数据,需要不断的轮询发出HTTP请求,占用大量带宽。
WebSocket采用了一些特殊的报头,使得浏览器和服务器只需要通过“握手”建立一条连接通道后,此链接保持活跃状态,之后的客户端和服务器的通信都使用这个连接,解决了Web实时性的问题,相比于HTTP有一下好处:

一个Web客户端只建立一个TCP连接
WebSocket服务端可以主动推送(push)数据到Web客户端
有更加轻量级的头,减少了数据传输量

特点

  1. 建立在TCP协议只上,服务端比较容易实现
  2. 于HTTP协议有良好的兼容性,默认端口也是80和443,握手阶段使用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器
  3. 数据格式轻量,通信高效且节省带宽
  4. 支持传输文本数据和二进制数据
  5. 没有同源限制,客户端可以与任意服务器通信
  6. 也支持加密传输,WS+SSL,URL形如wss://

技术

  • jdk8
  • maven
  • SpringBoot2.7.4
  • thmeleaf
  • websocket

实现(简易websocket聊天室)

pom.xml

<?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.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>websocket</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>websocket</name>
    <description>websocket</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

websocketHandler.java

package com.example.websocket.handler;


import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class WebsocketHandler implements WebSocketHandler {

    private static final Map<String, WebSocketSession> SESSIONS = new ConcurrentHashMap<>();
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("连接成功"+session.getId());
        SESSIONS.put(session.getId(), session);
        System.out.println("当前在线人数:"+SESSIONS.size());
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        System.out.println("接收消息"+session.getId());
        String msg = message.getPayload().toString();
        System.out.println(msg);
//        session.sendMessage(message);
        sendMessageToAllUsers(session.getId()+":"+msg);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("连接出错"+session.getId());
        if (!session.isOpen()) {
            SESSIONS.remove(session.getId());
            session.close();
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        System.out.println("关闭连接"+session.getId());
        if(!session.isOpen()){
            SESSIONS.remove(session.getId());
            System.out.println("当前在线人数:"+SESSIONS.size());
        }
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * sendMessageToUser:发给指定用户
     *
     */
    public void sendMessageToUser(String userId, String contents) {
        WebSocketSession session = SESSIONS.get(userId);
        if (session != null && session.isOpen()) {
            try {
                TextMessage message = new TextMessage(contents);
                session.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * sendMessageToAllUsers:发给所有的用户
     *
     */
    public void sendMessageToAllUsers(String contents) {
        Set<String> userIds = SESSIONS.keySet();
        for (String userId : userIds) {
            this.sendMessageToUser(userId, contents);
        }
    }
}

WebsocketInterceptor.java

package com.example.websocket.interceptor;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

@Component
public class WebsocketInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("前置拦截");
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        System.out.println("后置拦截");
    }
}

WebsocketConfig.java

package com.example.websocket.config;

import com.example.websocket.handler.WebsocketHandler;
import com.example.websocket.interceptor.WebsocketInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Component
@EnableWebSocket
public class WebsocketConfig implements WebSocketConfigurer {
    @Autowired
    private  WebsocketHandler websocketHandler;
    @Autowired
    private WebsocketInterceptor websocketInterceptor;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(websocketHandler, "/ws").setAllowedOrigins("*").addInterceptors(websocketInterceptor);
    }
}

WebsocketController.java

package com.example.websocket.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WebsocketController {

    @GetMapping("/")
    public String init() {
        return "websocket";
    }

    @GetMapping("/chat")
    public String chat() {
        return "chatRoom";
    }
}

WebsocketApplication.java

package com.example.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebsocketApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebsocketApplication.class, args);
    }

}

前端

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>My WebSocket Test</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">

    <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</head>
<body>

Welcome<br/>
<div id="message"></div>
<input id="text" type="text" />
<button class="btn btn-primary" onclick="send()">Send</button>
<button class="btn btn-danger"onclick="closeWebSocket()">Close</button>
</body>

<script type="text/javascript">

    var websocket = null;

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8080/ws");
    }
    else{
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }

    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
        message.value("");
    }
</script>
</html>

测试

先启动服务

然后我们就可以愉快聊天了

结尾

到这我们的简单版的ws长连接聊天室就做好了,欢迎大家点赞,关注涛哥!

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/LT19990304/article/details/129828762

SpringBoot + Vue + WebSocket 实时通信-爱代码爱编程

什么是websocket这里就不进行介绍了,有兴趣的可以自己百度,或许后面我也会发文章介绍。主要演示一下代码的实现,红色标注部分 需要格外注意 1、 引入依赖websocket <dependency> &

爬山的蜗牛旅程:十一、springboot +websocket+redis 实现分布式在线聊天_会飞的黑猪的博客-爱代码爱编程_springboot websocket redis 分布式

学习springboot的旅程,就像蜗牛爬山,一点点的往上爬,一点点的欣赏旅途的风景 小猿公司的系统又要升级了,现在要实现在线聊天。这个怎么搞呢? 特别是在分布式系统下,怎么搞分布式在线聊天呢? 第一点:客户端

netty+springboot+websocket+redis实现各个客户端之间通信、及时聊天等功能_邓12的博客-爱代码爱编程

说明:本文主要写的是即时聊天功能的一些设计思路,文末有完整项目地址,仅有后端代码。本人经验尚浅,有错误、不妥之处望各位大神与本人联系,不胜感激。文章中有些许借用的地方,如侵权,请及时联系本人,立马删除。 邮箱:2540628

springboot+websocket的实现-爱代码爱编程

本文章利用一个小的对话demo来展示springboot+websocket的实现。 下面开始简单的实现过程: 我的项目结构: 一、首先,创建springboot项目,在pox.xml中加入(下面是我的pom.xml的dependencies里的全部依赖,因为,这个是最简单的入门例子,所以只有主要的websocket和web依赖) &l

springboot+websocket实现聊天即时通讯-爱代码爱编程

springboot+websocket实现聊天即时通讯 1.在很多业务场景中,对实时数据要求比较高,我们就不能采用轮训拉取的方式来获取数据了。就可以采用websocket的长链接的形式,实时有服务端或者客户端推送数据,已达到数据的实时展示。 目录 WebSocketConfig package com.example.springboot_w

Java+Springboot+Websocket在线聊天室-爱代码爱编程

1、什么是websocket? websocket是由HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。它是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握

Springboot+Websocket+JWT实现的即时通讯模块-爱代码爱编程

场景 目前做了一个接口:邀请用户成为某课程的管理员,于是我感觉有能在用户被邀请之后能有个立马通知他本人的机(类似微博、朋友圈被点赞后就有立马能收到通知一样),于是就闲来没事搞了一套。 ​ 涉及技术栈 SpringbootWebsocket 协议JWT(非必要)RabbitMQ 消息中间件Websocket 协议 :star:推荐阅读: Webso

Springboot+vue+websocket 实现前后台主动通信-爱代码爱编程

后端springboot: pom <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-s

Springboot集成WebSocket实现即时通讯-爱代码爱编程

在SpringBoot的pom.xml文件里添加依赖: <!-- websocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-

springboot+websocket构建在线聊天室(群聊+单聊)_morou&猫猫的博客-爱代码爱编程

 系列导读: 1.springboot+websocket构建在线聊天室(群聊+单聊) 2.Spring Boot WebSocket:单聊(实现思路) 3.Websocket Stomp+RabbitMQ实现消息推送 目录 1.群聊+显示昵称 1.1、群聊 功能实现 1.1.1、引入依赖 1.1.2、注入ServerEndpointE

springboot+netty+websocket实现简单的在线聊天小功能_这里是杨杨吖的博客-爱代码爱编程

效果演示视频和教学讲解视频地址:演示地址 注意:先启动SpringBoot项目,再启动WebSocketServer!!! 代码: 1.pom.xml代码: <?xml version="1.0" encodin

springboot+websocket实现即时通讯(j2ee方式)_java技术那些事儿的博客-爱代码爱编程

什么是websocket? WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSock

springboot+vue+websocket实现前后端即时通知-爱代码爱编程

准备 后端引入pom依赖 <dependency> <groupId>org.springframework</groupId> &