Java中的WecSocket

目录

零、码仙励志

一、WebSocket概述

1.为什么要用WebSocket

2.什么是WebSocket

3.最低支持要求

二、Tomcat实现WebSocket

1.简单实现

2.连接的时候传入参数

3.群聊演示

4.单聊演示

三、SpringBoot整合WebSocket

1.基于注解的方式

1.群聊演示

2.单聊演示

2.基于实现类的方式

1.简单实现

2.连接的时候传入参数

3.群聊演示

4.单聊演示

5.SockJs

3.基于STOMP协议的方式

1.后端代码

2.前端代码

四、参考文献


零、码仙励志

失败的是事,绝不应是人。

一、WebSocket概述

1.为什么要用WebSocket

传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式 对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应 用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)和Comet技术。其实后者本质上也是一种轮询,只不过有所改进。

轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。

Comet技术又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。

这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。

2.什么是WebSocket

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

WebSocket 使用了 HTTP/1.1 的协议升级特性,一个 WebSocket 请求首先使用非正常的 HTTP 请求以特定的模式访问一个 URL ,这个 URL 有两种模式,分别是 ws 和 wss ,对应 HTTP 协议中的 HTTP和HTTPS ,在请求头中有一个 Connection:Upgrade 字段,表示客户端想要对协议进行升级,另外还有一个 Upgrade:websocket 字段,表示客户端想要将请求协议升级为 WebSocket 协议。 这两个字段共同告诉服务器要将连接升级为 WebSocket 这样 一种全双工协议,如果服务端同意协议升级,那么在握手完成之后,文本消息或者其他二进制消息就可以同时在两个方向上进行发送,而不需要关闭和重建连接。 此时的客户端和服务端关系是对等的,它们可以互相向对方主动发送消 息。和传统的解决方案相比, WebSocket 主要有如下特点:

  1. WebSocket 使用时需要先创建连接,这使得 WebSocket 成为一种有状态的协议,在之后的通信过程中可以省略部分状态信息(例如身份认证等)。

  2. WebSocket 连接在端口 80 ( WS )或者 443 ( wss )上创建,与 HTTP 使用的端口相同,这样,基本上所有的防火墙都不会阻止 WebSocket 连接。

  3. WebSocket 使用 HTTP 协议进行握手,因此它可以自然而然地集成到网络浏览器和 HTTP 服务器中,而不需要额外的成本。

  4. 心跳消息(ping 和 pong)将被反复的发送,进而保持 WebSocket 连接一直处于活跃状态。

  5. 使用该协议,当消息启动或者到达的时候,服务端和客户端都可以知道。

  6. WebSocket 连接关闭时将发送一个特殊的关闭消息。

  7. WebSocket 支持跨域,可以避免 Ajax的限制。

  8. HTTP 规范要求浏览器将并发连接数限制为每个主机名两个连接,但是当我们使用 WebSocket 的时候,当握手完成之后,该限制就不存在了,因为此时的连接已经不再是 HTTP 连接了。

  9. WebSocket 协议支持扩展,用户可以扩展协议,实现部分自定义的子协议。

  10. 更好的二进制支持以及更好的压缩效果。

WebSocket 既然具有这么多优势,使用场景当然也是非常广泛的,例如:

  1. 在线股票网站。

  2. 即时聊天。

  3. 多人在线游戏。

  4. 应用集群通信。

  5. 系统性能实时监控。

3.最低支持要求

浏览器支持:所有的最新浏览器支持最新WebSocket规范(RFC 6455) ,从维基百科上介绍浏览器对WebSocket的支持如下表所示:

浏览器 Chrome Edge Firfox IE Opera Safari
最低版本 16 支持 11.0 10 12.10 6.0

移动端支持:移动端基本都支持websocket了,其实和浏览器版支持的版本一样,具体支持如下所示:

最低 android浏览器 Chrome 移动版 Firfox 移动版 Opera 移动版 Safari IOS版
最低版本 4.4 16 11.0 12.10 6.0

服务器支持:目前主流的web服务器都已经支持,具体版本如下表所示:

厂商 应用服务器 备注
IBM WebSphere WebSphere 8.0 以上版本支持,7.X 之前版本结合 MQTT 支持类似的 HTTP 长连接
甲骨文 WebLogic WebLogic 12c 支持,11g 及 10g 版本通过 HTTP Publish 支持类似的 HTTP 长连接
微软 IIS IIS 7.0+支持
Apache Tomcat Tomcat 7.0.5+支持,7.0.2X 及 7.0.3X 通过自定义 API 支持
Apache Jetty Jetty 7.0+支持

二、Tomcat实现WebSocket

引入websocket依赖


    javax.websocket
    javax.websocket-api
    1.1
    provided

建立一个用来跳转连接的页面

index.html




    
    Title


Easy
EasyPar
GroupChat
SingleChat

1.简单实现

服务端代码(Easy.java)

package com.mahaiwuji.socket;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/easy")
public class Easy {

    /**
     * 连接时执行
     *
     * @param session
     */
    @OnOpen
    public void onOPen(Session session) {
        System.out.println("连接成功");
    }

    /**
     * 收到消息时执行
     *
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println(message);
        session.getBasicRemote().sendText("收到消息");
    }

    /**
     * 关闭时执行
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("连接关闭");
    }

    /**
     * 连接错误时执行
     *
     * @param session
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
    }

}

客户端代码(easy.html)




    
    Title


Easy

消息:

2.连接的时候传入参数

服务端代码(EasyPar.java)

package com.mahaiwuji.socket;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/easyPar/{username}/{sex}")
public class EasyPar {

    /**
     * 连接时执行
     *
     * @param session
     */
    @OnOpen
    public void onOPen(@PathParam("username") String username, @PathParam("sex") String sex, Session session) {
        System.out.println("连接成功");
        System.out.println(username);
        System.out.println(sex);
    }

    /**
     * 收到消息时执行
     *
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println(message);
        session.getBasicRemote().sendText("收到消息");
    }

    /**
     * 关闭时执行
     */
    @OnClose
    public void onClose(Session session) {
        System.out.println("连接关闭");
    }

    /**
     * 连接错误时执行
     *
     * @param session
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
    }

}

客户端代码(easyPar.html)




    
    Title


EasyPar

消息:

3.群聊演示

服务端代码(GroupChat.java)

package com.mahaiwuji.socket;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@ServerEndpoint("/groupChat/{username}")
public class GroupChat {

    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static final Map users = new HashMap();

    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String username;

    /**
     * 连接时执行
     *
     * @param username
     * @param session
     */
    @OnOpen
    public void onOPen(@PathParam("username") String username, Session session) {
        this.username = username;
        this.session = session;
        users.put(username, session); // 加入Map中
        System.out.println("用户登录:" + username);
        addOnlineCount(); // 在线数加1
        System.out.println("当前在线人数:" + onlineCount);
    }

    /**
     * 收到消息时执行
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        boolean flag = sendMessageToAllUsers(username + ":" + message);
        if (flag) {
            System.out.println("群发成功");
        }
    }

    /**
     * 关闭时执行
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        users.remove(username); // 从Map中删除
        System.out.println("用户退出:" + username);
        subOnlineCount(); // 在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接错误时执行
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        GroupChat.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        GroupChat.onlineCount--;
    }

    /**
     * 群发
     *
     * @param message
     * @return
     */
    public boolean sendMessageToAllUsers(String message) {
        boolean allSendSuccess = true;
        Set usernames = users.keySet();
        Session session = null;
        for (String username : usernames) {
            try {
                session = users.get(username);
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                } else {
                    System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
                }
            } catch (IOException e) {
                System.out.println("群发失败");
                allSendSuccess = false;
            }
        }
        return allSendSuccess;
    }
}

客户端代码(groupchat.html)




    
    Title


GroupChat

消息:

4.单聊演示

引入json依赖


    com.alibaba
    fastjson
    1.2.47

服务端代码(SingleChat.java)

package com.mahaiwuji.socket;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@ServerEndpoint("/singleChat/{username}")
public class SingleChat {

    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static final Map users = new HashMap();

    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String username;

    /**
     * 连接时执行
     *
     * @param username
     * @param session
     */
    @OnOpen
    public void onOPen(@PathParam("username") String username, Session session) {
        this.username = username;
        this.session = session;
        users.put(username, session); // 加入Map中
        System.out.println("用户登录:" + username);
        addOnlineCount(); // 在线数加1
        System.out.println("当前在线人数:" + onlineCount);
    }

    /**
     * 收到消息时执行
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println(message);
        JSONObject parseObject = JSONObject.parseObject(message);
        Object nameObject = parseObject.get("name");
        Object msgObject = parseObject.get("msg");
        String name = JSON.toJSONString(nameObject);
        String msg = JSON.toJSONString(msgObject);
        name = name.substring(1, name.length() - 1);
        msg = msg.substring(1, msg.length() - 1);
        System.out.println(name);
        System.out.println(msg);
        boolean flag = sendMessageToUser(name, username + ":" + msg);
        if (flag) {
            //发送成功以后让自己也能看到消息
            sendMessageToUser(username, "我:" + msg);
        } else {
            //发送失败给发送者提示
            sendMessageToUser(username, "给" + name + "发送消息失败");
        }
    }

    /**
     * 关闭时执行
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        users.remove(username); // 从Map中删除
        System.out.println("用户退出:" + username);
        subOnlineCount(); // 在线数减1
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接错误时执行
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        SingleChat.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        SingleChat.onlineCount--;
    }

    /**
     * 发送信息给指定用户
     *
     * @param username
     * @param message
     * @return
     */
    public boolean sendMessageToUser(String username, String message) {
        if (users.get(username) == null)
            return false;
        Session session = users.get(username);
        if (!session.isOpen()) {
            System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
            return false;
        }
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            System.out.println("发送失败");
            return false;
        }
        return true;
    }

}

客户端代码(singlechat.html)




    
    Title


SingleChat

对方用户名: 消息:

三、SpringBoot整合WebSocket

1.基于注解的方式

启动类

AnnotationSocketApplication.java

package com.mahaiwuji;

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

@SpringBootApplication
public class AnnotationSocketApplication {

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

}

引入websocket依赖


    org.springframework.boot
    spring-boot-starter-websocket

创建节点配置类

SocketConfig.java

package com.mahaiwuji.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class SocketConfig {
    //这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket,如果你使用外置的tomcat就不需要该配置文件
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

建立一个用来跳转连接的页面

index.html




    
    Title


GroupChat
SingleChat

1.群聊演示

服务端代码(GroupChat.java)

package com.mahaiwuji.socket;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
@ServerEndpoint("/groupChat/{username}")
public class GroupChat {

    // 用来存放每个客户端对应的Session对象。
    private static final Map users = new HashMap();

    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String username;

    /**
     * 连接时执行
     *
     * @param username
     * @param session
     */
    @OnOpen
    public void onOPen(@PathParam("username") String username, Session session) {
        this.username = username;
        this.session = session;
        users.put(username, session); // 加入Map中
        System.out.println("用户登录:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 收到消息时执行
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        boolean flag = sendMessageToAllUsers(username + ":" + message);
        if (flag) {
            System.out.println("群发成功");
        }
    }

    /**
     * 关闭时执行
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        users.remove(username); // 从Map中删除
        System.out.println("用户退出:" + username);
        System.out.println("有一连接关闭!当前在线人数为" + users.size());
    }

    /**
     * 连接错误时执行
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 群发
     *
     * @param message
     * @return
     */
    public boolean sendMessageToAllUsers(String message) {
        boolean allSendSuccess = true;
        Set usernames = users.keySet();
        Session session = null;
        for (String username : usernames) {
            try {
                session = users.get(username);
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                } else {
                    System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
                }
            } catch (IOException e) {
                System.out.println("群发失败");
                allSendSuccess = false;
            }
        }
        return allSendSuccess;
    }
}

客户端代码(groupchat.html)




    
    Title


GroupChat

消息:

2.单聊演示

引入json依赖


    com.alibaba
    fastjson
    1.2.47

服务端代码(SingleChat.java)

package com.mahaiwuji.socket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
@ServerEndpoint("/singleChat/{username}")
public class SingleChat {

    // 用来存放每个客户端对应的Session对象。
    private static final Map users = new HashMap();

    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String username;

    /**
     * 连接时执行
     *
     * @param username
     * @param session
     */
    @OnOpen
    public void onOPen(@PathParam("username") String username, Session session) {
        this.username = username;
        this.session = session;
        users.put(username, session); // 加入Map中
        System.out.println("用户登录:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 收到消息时执行
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println(message);
        JSONObject parseObject = JSONObject.parseObject(message);
        Object nameObject = parseObject.get("name");
        Object msgObject = parseObject.get("msg");
        String name = JSON.toJSONString(nameObject);
        String msg = JSON.toJSONString(msgObject);
        name = name.substring(1, name.length() - 1);
        msg = msg.substring(1, msg.length() - 1);
        System.out.println(name);
        System.out.println(msg);
        boolean flag = sendMessageToUser(name, username + ":" + msg);
        if (flag) {
            //发送成功以后让自己也能看到消息
            sendMessageToUser(username, "我:" + msg);
        } else {
            //发送失败给发送者提示
            sendMessageToUser(username, "给" + name + "发送消息失败");
        }
    }

    /**
     * 关闭时执行
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        users.remove(username); // 从Map中删除
        System.out.println("用户退出:" + username);
        System.out.println("有一连接关闭!当前在线人数为" + users.size());
    }

    /**
     * 连接错误时执行
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 发送信息给指定用户
     *
     * @param username
     * @param message
     * @return
     */
    public boolean sendMessageToUser(String username, String message) {
        if (users.get(username) == null)
            return false;
        Session session = users.get(username);
        if (!session.isOpen()) {
            System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
            return false;
        }
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            System.out.println("发送失败");
            return false;
        }
        return true;
    }

}

客户端代码(singlechat.html)




    
    Title


SingleChat

对方用户名: 消息:

2.基于实现类的方式

启动类

ImplementsSocketApplication.java

package com.mahaiwuji;

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

@SpringBootApplication
public class ImplementsSocketApplication {

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

}

引入websocket依赖


    org.springframework.boot
    spring-boot-starter-websocket

创建节点配置类

SocketConfig.java

package com.mahaiwuji.config;

import com.mahaiwuji.interceptor.*;
import com.mahaiwuji.socket.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * 通过实现WebSocketConfigurer接口,可以注册相应的WebSocket处理器、路径、允许域、SockJs支持。
 */
@Configuration
@EnableWebSocket
public class SocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    }
}

建立一个用来跳转连接的页面

index.html




    
    Title


Easy
EasyPar
GroupChat
SingleChat
SockJs

1.简单实现

服务端代码(Easy.java)

package com.mahaiwuji.socket;

import org.springframework.web.socket.*;

public class Easy implements WebSocketHandler {

    /**
     * 连接时执行
     *
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        System.out.println("连接成功");
    }

    /**
     * 收到消息时执行
     *
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage webSocketMessage) throws Exception {
        // 获取beforeHandshake中map放入的值
        String name = (String) webSocketSession.getAttributes().get("name");
        System.out.println(name);
        // 获取客户端发送的内容
        System.out.println(webSocketMessage);
        String text = (String) webSocketMessage.getPayload();
        System.out.println(text);
        // 给客户端发送消息
        webSocketSession.sendMessage(new TextMessage("收到消息"));
    }

    /**
     * 关闭时执行
     *
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        System.out.println("连接关闭");
    }

    /**
     * 连接错误时执行
     *
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("发生错误");
    }

    /**
     * 是否处理部分消息,在连接前执行
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        System.out.println("supportsPartialMessages");
        return false;
    }
}

服务端拦截器代码(EasyInterceptor.java)

package com.mahaiwuji.interceptor;

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

import java.util.Map;

/**
 * HandshakeInterceptor接口是WebSocket连接握手过程的拦截器,通过实现该接口可以对握手过程进行管理。
 * 值得注意的是,beforeHandshake中的map与WebSocketSession中通过getAttributes();
 * 返回的Map是同一个Map,我们可以在其中放入一些用户的特定信息。
 */
public class EasyInterceptor implements HandshakeInterceptor {

    /**
     * 握手前,在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
        //这里给map中放入一个值,在WebSocketSession中获取
        map.put("name", "码海无际");
        System.out.println("握手前");
        return true;
    }

    /**
     * 握手后,也在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("握手后");
    }
}

服务端节点配置类SocketConfig.java

package com.mahaiwuji.config;

import com.mahaiwuji.interceptor.*;
import com.mahaiwuji.socket.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * 通过实现WebSocketConfigurer接口,可以注册相应的WebSocket处理器、路径、允许域、SockJs支持。
 */
@Configuration
@EnableWebSocket
public class SocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new Easy(), "/easy").addInterceptors(new EasyInterceptor()).setAllowedOrigins("*");
    }
}

客户端代码(easy.html)




    
    Title


Easy

消息:

2.连接的时候传入参数

服务端代码(EasyPar.java)

package com.mahaiwuji.socket;

import org.springframework.web.socket.*;

public class EasyPar implements WebSocketHandler {

    /**
     * 连接时执行
     *
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        System.out.println("连接成功");
    }

    /**
     * 收到消息时执行
     *
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage webSocketMessage) throws Exception {
        // 获取beforeHandshake中map放入的值
        String username = (String) webSocketSession.getAttributes().get("username");
        System.out.println(username);
        // 获取客户端发送的内容
        System.out.println(webSocketMessage);
        String text = (String) webSocketMessage.getPayload();
        System.out.println(text);
        // 给客户端发送消息
        webSocketSession.sendMessage(new TextMessage("收到消息"));
    }

    /**
     * 关闭时执行
     *
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        System.out.println("连接关闭");
    }

    /**
     * 连接错误时执行
     *
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("发生错误");
    }

    /**
     * 是否处理部分消息,在连接前执行
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        System.out.println("supportsPartialMessages");
        return false;
    }
}

服务端拦截器代码(EasyParInterceptor.java)

package com.mahaiwuji.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Map;

/**
 * HandshakeInterceptor接口是WebSocket连接握手过程的拦截器,通过实现该接口可以对握手过程进行管理。
 * 值得注意的是,beforeHandshake中的map与WebSocketSession中通过getAttributes();
 * 返回的Map是同一个Map,我们可以在其中放入一些用户的特定信息。
 */
public class EasyParInterceptor implements HandshakeInterceptor {

    /**
     * 握手前,在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
        System.out.println("握手前");
        HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
        String username = httpServletRequest.getParameter("username");
        System.out.println(username);
        String sex = httpServletRequest.getParameter("sex");
        System.out.println(sex);
        //这里给map中放入一个值,在WebSocketSession中获取
        map.put("username", username);
        return true;
    }

    /**
     * 握手后,也在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("握手后");
    }
}

服务端节点配置类SocketConfig.java

在registerWebSocketHandlers方法中添加以下代码:

registry.addHandler(new EasyPar(), "/easyPar").addInterceptors(new EasyParInterceptor()).setAllowedOrigins("*");

客户端代码(easyPar.html)




    
    Title


EasyPar

消息:

3.群聊演示

服务端代码(GroupChat.java)

package com.mahaiwuji.socket;

import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class GroupChat implements WebSocketHandler {

    // 用来存放每个客户端对应的Session对象。
    private static final Map users = new HashMap();

    /**
     * 连接时执行
     *
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        System.out.println("连接成功");
        String username = (String) webSocketSession.getAttributes().get("username");
        users.put(username, webSocketSession);
        System.out.println("登录用户:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 收到消息时执行
     *
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage webSocketMessage) throws Exception {
        // 获取beforeHandshake中map放入的值
        String username = (String) webSocketSession.getAttributes().get("username");
        System.out.println("发送消息用户:" + username);
        // 获取客户端发送的内容
        System.out.println(webSocketMessage);
        String message = (String) webSocketMessage.getPayload();
        System.out.println(message);
        boolean flag = sendMessageToAllUsers(username + ":" + message);
        if (flag) {
            System.out.println("群发成功");
        }
    }

    /**
     * 关闭时执行
     *
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        System.out.println("连接关闭");
        String username = (String) webSocketSession.getAttributes().get("username");
        users.remove(username);
        System.out.println("退出用户:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 连接错误时执行
     *
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("发生错误");
    }

    /**
     * 是否处理部分消息,在连接前执行
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        System.out.println("supportsPartialMessages");
        return false;
    }

    /**
     * 群发
     *
     * @param message
     * @return
     */
    public boolean sendMessageToAllUsers(String message) {
        boolean allSendSuccess = true;
        Set usernames = users.keySet();
        WebSocketSession session = null;
        for (String username : usernames) {
            try {
                session = users.get(username);
                if (session.isOpen()) {
                    session.sendMessage(new TextMessage(message));
                } else {
                    System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
                }
            } catch (IOException e) {
                System.out.println("群发失败");
                allSendSuccess = false;
            }
        }
        return allSendSuccess;
    }
}

服务端拦截器代码(GroupChatInterceptor.java)

package com.mahaiwuji.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * HandshakeInterceptor接口是WebSocket连接握手过程的拦截器,通过实现该接口可以对握手过程进行管理。
 * 值得注意的是,beforeHandshake中的map与WebSocketSession中通过getAttributes();
 * 返回的Map是同一个Map,我们可以在其中放入一些用户的特定信息。
 */
public class GroupChatInterceptor implements HandshakeInterceptor {

    /**
     * 握手前,在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
        System.out.println("握手前");
        HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
        String username = httpServletRequest.getParameter("username");
        System.out.println(username);
        //这里给map中放入一个值,在WebSocketSession中获取
        map.put("username", username);
        return true;
    }

    /**
     * 握手后,也在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("握手后");
    }
}

 服务端节点配置类SocketConfig.java

在registerWebSocketHandlers方法中添加以下代码:

registry.addHandler(new GroupChat(), "/groupChat").addInterceptors(new GroupChatInterceptor()).setAllowedOrigins("*");

客户端代码(groupchat.html)




    
    Title


GroupChat

消息:

4.单聊演示

引入json依赖


    com.alibaba
    fastjson
    1.2.47

服务端代码(SingleChat.java)

package com.mahaiwuji.socket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.socket.*;

import javax.websocket.Session;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SingleChat implements WebSocketHandler {

    // 用来存放每个客户端对应的Session对象。
    private static final Map users = new HashMap();

    /**
     * 连接时执行
     *
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        System.out.println("连接成功");
        String username = (String) webSocketSession.getAttributes().get("username");
        users.put(username, webSocketSession);
        System.out.println("登录用户:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 收到消息时执行
     *
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage webSocketMessage) throws Exception {
        // 获取beforeHandshake中map放入的值
        String username = (String) webSocketSession.getAttributes().get("username");
        System.out.println("发送消息用户:" + username);
        // 获取客户端发送的内容
        System.out.println(webSocketMessage);
        String message = (String) webSocketMessage.getPayload();
        System.out.println(message);
        // 解析客户端发送的内容
        JSONObject parseObject = JSONObject.parseObject(message);
        Object nameObject = parseObject.get("name");
        Object msgObject = parseObject.get("msg");
        String name = JSON.toJSONString(nameObject);
        String msg = JSON.toJSONString(msgObject);
        name = name.substring(1, name.length() - 1);
        msg = msg.substring(1, msg.length() - 1);
        System.out.println(name);
        System.out.println(msg);
        boolean flag = sendMessageToUser(name, username + ":" + msg);
        if (flag) {
            //发送成功以后让自己也能看到消息
            sendMessageToUser(username, "我:" + msg);
        } else {
            //发送失败给发送者提示
            sendMessageToUser(username, "给" + name + "发送消息失败");
        }
    }

    /**
     * 关闭时执行
     *
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        System.out.println("连接关闭");
        String username = (String) webSocketSession.getAttributes().get("username");
        users.remove(username);
        System.out.println("退出用户:" + username);
        System.out.println("当前在线人数:" + users.size());
    }

    /**
     * 连接错误时执行
     *
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("发生错误");
    }

    /**
     * 是否处理部分消息,在连接前执行
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        System.out.println("supportsPartialMessages");
        return false;
    }

    /**
     * 发送信息给指定用户
     *
     * @param username
     * @param message
     * @return
     */
    public boolean sendMessageToUser(String username, String message) {
        if (users.get(username) == null)
            return false;
        WebSocketSession session = users.get(username);
        if (!session.isOpen()) {
            System.out.println("客户端:" + username + ",已断开连接,发送消息失败");
            return false;
        }
        try {
            session.sendMessage(new TextMessage(message));
        } catch (IOException e) {
            System.out.println("发送失败");
            return false;
        }
        return true;
    }
}

 服务端拦截器代码(SingleChatInterceptor.java)

package com.mahaiwuji.interceptor;

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

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * HandshakeInterceptor接口是WebSocket连接握手过程的拦截器,通过实现该接口可以对握手过程进行管理。
 * 值得注意的是,beforeHandshake中的map与WebSocketSession中通过getAttributes();
 * 返回的Map是同一个Map,我们可以在其中放入一些用户的特定信息。
 */
public class SingleChatInterceptor implements HandshakeInterceptor {

    /**
     * 握手前,在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
        System.out.println("握手前");
        HttpServletRequest httpServletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
        String username = httpServletRequest.getParameter("username");
        System.out.println(username);
        //这里给map中放入一个值,在WebSocketSession中获取
        map.put("username", username);
        return true;
    }

    /**
     * 握手后,也在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("握手后");
    }
}

服务端节点配置类SocketConfig.java

在registerWebSocketHandlers方法中添加以下代码:

registry.addHandler(new SingleChat(), "/singleChat").addInterceptors(new SingleChatInterceptor()).setAllowedOrigins("*");

 客户端代码(singlechat.html)




    
    Title


SingleChat

对方用户名: 消息:

5.SockJs

什么是SockJs:有一些浏览器中缺少对WebSocket的支持,而SockJS是一个浏览器的JavaScript库,它提供了一个类似于网络的对象,SockJS提供了一个连贯的,跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。SockJS的一大好处在于提供了浏览器兼容性。即优先使用原生WebSocket,如果浏览器不支持WebSocket,会自动降为轮询的方式。

服务端代码(SockJs.java)

package com.mahaiwuji.socket;

import org.springframework.web.socket.*;

public class SockJs implements WebSocketHandler {

    /**
     * 连接时执行
     *
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        System.out.println("连接成功");
    }

    /**
     * 收到消息时执行
     *
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage webSocketMessage) throws Exception {
        // 获取beforeHandshake中map放入的值
        String name = (String) webSocketSession.getAttributes().get("name");
        System.out.println(name);
        // 获取客户端发送的内容
        System.out.println(webSocketMessage);
        String text = (String) webSocketMessage.getPayload();
        System.out.println(text);
        // 给客户端发送消息
        webSocketSession.sendMessage(new TextMessage("收到消息la"));
    }

    /**
     * 关闭时执行
     *
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        System.out.println("连接关闭");
    }

    /**
     * 连接错误时执行
     *
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        System.out.println("发生错误");
    }

    /**
     * 是否处理部分消息,在连接前执行
     *
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        System.out.println("supportsPartialMessages");
        return false;
    }
}

服务端拦截器代码(SockJsInterceptor.java)

package com.mahaiwuji.interceptor;

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

import java.util.Map;

/**
 * HandshakeInterceptor接口是WebSocket连接握手过程的拦截器,通过实现该接口可以对握手过程进行管理。
 * 值得注意的是,beforeHandshake中的map与WebSocketSession中通过getAttributes();
 * 返回的Map是同一个Map,我们可以在其中放入一些用户的特定信息。
 */
public class SockJsInterceptor implements HandshakeInterceptor {

    /**
     * 握手前,在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throws Exception {
        //这里给map中放入一个值,在WebSocketSession中获取
        map.put("name", "码海无际");
        System.out.println("握手前");
        return true;
    }

    /**
     * 握手后,也在连接前执行,并且只在每次连接前执行
     *
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        System.out.println("握手后");
    }
}

 服务端节点配置类SocketConfig.java

在registerWebSocketHandlers方法中添加以下代码:

registry.addHandler(new SockJs(), "/sockJs").addInterceptors(new SockJsInterceptor()).setAllowedOrigins("*");
registry.addHandler(new SockJs(), "/sockJs").addInterceptors(new SockJsInterceptor()).setAllowedOrigins("*").withSockJS();

客户端代码(sockjs.html)




    
    Title


SockJs

消息:

3.基于STOMP协议的方式

1.后端代码

启动类

StompSocketApplication.java

package com.mahaiwuji;

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

@SpringBootApplication
public class StompSocketApplication {

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

}

引入websocket依赖


    org.springframework.boot
    spring-boot-starter-websocket

创建节点配置类

WebSocketConfig.java

package com.mahaiwuji.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker  //注解开启STOMP协议来传输基于代理的消息,此时控制器支持使用@MessageMapping
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //groupChatContent用来群聊,user用来单聊,单聊必须用user
        config.enableSimpleBroker("/groupChatContent", "/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //用于群聊连接
        registry.addEndpoint("/groupChat").withSockJS();
        //用于群聊连接
        registry.addEndpoint("/singleChat").withSockJS();
    }

}

 创建实体类,用来传输数据

Chat.java

package com.mahaiwuji.pojo;

public class Chat {

    // 发送消息的用户名
    private String toUsername;
    // 接收消息的用户名
    private String fromUsername;
    // 发送的内容
    private String content;

    public String getToUsername() {
        return toUsername;
    }

    public void setToUsername(String toUsername) {
        this.toUsername = toUsername;
    }

    public String getFromUsername() {
        return fromUsername;
    }

    public void setFromUsername(String fromUsername) {
        this.fromUsername = fromUsername;
    }

    public String getContent() {
        return content;
    }

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

    @Override
    public String toString() {
        return "Chat{" +
                "toUsername='" + toUsername + '\'' +
                ", fromUsername='" + fromUsername + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

创建与前端页面交互的Controller

ChatController.java

package com.mahaiwuji.controller;

import com.mahaiwuji.pojo.Chat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

@Controller
public class ChatController {

    @Autowired
    public SimpMessagingTemplate template;

    /**
     * 群聊
     *
     * @param chat
     */
    @MessageMapping("/sendGroupChat")
    public void sendGroupChat(Chat chat) {
        System.out.println(chat);
        //群聊使用convertAndSend方法,第一个参数为目的地,和js中订阅的目的地要一致
        template.convertAndSend("/groupChatContent", chat.getToUsername() + ":" + chat.getContent());
    }

    @MessageMapping("/sendSingleChat")
    public void sendSingleChat(Chat chat) {
        System.out.println(chat);
        // 单聊使用convertAndSendToUser方法
        // 第一个参数为用户id
        // 此时js中的订阅地址为"/user/" + 接收消息用户 + "/singleChatContent",其中"/user"是固定的
        // convertAndSendToUser底层调用了convertAndSend
        template.convertAndSendToUser(chat.getFromUsername(), "/singleChatContent", chat.getToUsername() + ":" + chat.getContent());
        //让自己也能看到消息
        template.convertAndSendToUser(chat.getToUsername(), "/singleChatContent", "我:" + chat.getContent());
    }
}

2.前端代码

建立一个用来跳转连接的页面

index.html




    
    Title


GroupChat
SingleChat

群聊页面

groupchat.html




    
    Title


GroupChat

消息:

单聊页面

singlechat.html




    
    Title


SingleChat

对方用户名: 消息:

四、参考文献

https://www.cnblogs.com/onlymate/p/9521327.html

https://www.jianshu.com/p/d79bf8174196

https://blog.csdn.net/huiyunfei/article/details/90719351

https://www.cnblogs.com/hhhshct/p/8849449.html

https://www.cnblogs.com/jmcui/p/8999998.html

https://blog.csdn.net/qq_35387940/article/details/93483678

https://www.jianshu.com/p/4ce6cb1310e6

《Spring Boot Vue全栈开发实战》- 王松

 

 

你可能感兴趣的:(Javase)