基于WebSocket实现一个简易的群聊功能

2022-07-27 13:55:13 浏览数 (1)

本文主要来讲解如何使用WebSocket来实现一个简易的群聊功能。

引入maven依赖

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

项目的目录结构

接下来,我们来分析每个类的作用。

WebSocketApplication

代码语言:javascript复制
@SpringBootApplication
public class WebSocketApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebSocketApplication.class, args);
    }
}

这个类就是一个简单的启动引导类的功能。

WebSocketServer:群聊核心类

代码语言:javascript复制
@Component
@ServerEndpoint(value = "/chat/{username}", configurator = SpringBasedConfigurator.class)
public class WebSocketServer {
    private final Map<Session, String> userSessionMap = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {
        userSessionMap.put(session, username);
        multicastMessage(session, "欢迎"   username   "加入群聊!");
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        String username = userSessionMap.get(session);
        multicastMessage(session, username   ": "   message);
    }

    @OnClose
    public void onClose(Session session) {
        String username = userSessionMap.remove(session);
        multicastMessage(session, username   "退出群聊。");
    }

    private void multicastMessage(Session session, String message) {
        for (Session userSession : userSessionMap.keySet()) {
            if (userSession == session) {
                //是自己,忽略
                continue;
            }
            try {
                userSession.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

@ServerEndpoint:作用是用来表明当前类是一个节点类,当连接成功之后,用户的操作都会回调这个类对应的对象的方法,对象怎么创建的是根据configurator 属性对应的类来实现的,默认是每个连接对应的对象都是通过反射构建的,但是对于本群聊功能来说,是通过SpringBasedConfigurator对象来获取,这个类的作用接下来会剖析。

@OnOpen:当客户端与服务端建立连接的时候,会回调 @OnOpen 注解标记的方法

@PathParam:可以看成跟spring mvc中的@PathVariable注解作用一样,就是取出连接路径中的占位符对应的值

@OnMessage:当客户端发送消息给服务端的时候,会回调OnMessage 注解标记的方法

@OnClose:当客户端断开连接的时候,会回调@OnClose注解标记的方法。

Session:是代表当前客户端与服务端建立的一个会话,通过这个对象,服务端可以主动给客户端发送消息。

WebSocketServer这个类的主要作用是保存每个客户端与服务端建立的连接,一旦有客户端跟服务端建立连接、发送消息、断开连接,都会发送消息给其他客户端,从而实现群聊的功能。

SpringBasedConfigurator

代码语言:javascript复制
@Component
public class SpringBasedConfigurator extends ServerEndpointConfig.Configurator implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringBasedConfigurator.applicationContext = applicationContext;
    }

    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return applicationContext.getBean(clazz);
    }

}

这个类是继承了WebSocket包提供的api ServerEndpointConfig.Configurator ,重写了 getEndpointInstance 方法,主要是因为WebSocket是通过调用getEndpointInstance方法来获取每个连接对应调用的对象, 而getEndpointInstance方法默认是通过直接通过反射构造的,而不是从spring容器获取连接对象,导致类中的像@Resource 这类注解无法生效,所以重写了getEndpointInstance方法,让每个连接对应调用的对象都是从spring容器中获取

WebSocketConfiguration:配置类

代码语言:javascript复制
@Configuration
public class WebSocketConfiguration {

    /**
     * 这个类的主要注册每个加了{@link javax.websocket.server.ServerEndpoint}的 spring bean节点,这算是spring整合websocket的一个体现
     * 具体是怎么实现注册的,可以看看 {@link ServerEndpointExporter#afterSingletonsInstantiated()}方法的实现
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

}

暴露每个加@ServerEndpoint注解的spring bean,算是spring跟WebSocket整合的一处体现。

测试

好了,说完这些类的功能,那么开启测试吧。WebSocket客户端,我们使用https://www.idcd.com/tool/socket网站来模拟。

通过启动引导类来启动项目之后,我们输入 ws://localhost:8080/chat/sanyou, 建立一个连接,模拟一个客户端。

输入连接地址,点击连接,就会显示连接成功,username这里我们填sanyou,填什么都无所谓,相当于一个名字。

我们再建立一个连接,模拟另一个客户端。

第一个客户端就会显示这条信息。

接下来,就可以在发送栏往服务端发送消息,服务端会转给其他的客户端,实现群聊的功能,效果如下。

如果还想加入群聊,另外建立连接就行了,这样,一个简易的群聊功能就完成了。

以上就是本篇文章的全部内容,代码我已经上传到https://github.com/sanyou3/sanyou-parent.git 仓库上了,有更详细的注释,我们下篇文章再见。

0 人点赞