摘要

公司项目中有使用到WebSocket,所以就打算研究一下。

之前用原生的io流实现了一个聊天室,简单的上线下线通知,外加聊天,就用到了io、线程、线程锁,没想到现在实现一个聊天室变得这么简单。

正文

参考文章

先放上一个坑的解决办法吧。

我用Java开启一个WebSocket服务器,通过localhost是可以连接的,通过127也可以,但是换成局域网ip之后,就连不上了。

这个坑,大概花了我一周的时间,原因很简单。比方说我的宽带、wlan、vmware虚拟机,都有一个局域网ip,如下图。

1.png

通过http,不管走哪一个局域网ip,都能访问到服务器上的页面,但是websocket不一样,websocket服务器启动之后,只能绑定一个ip,至于绑定的哪一个,不知道。所以如果想要用指定的局域网ip进行测试时,可以把其他的网络适配器禁用。

WebSocketConfig

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    /**
     * 注册STOMP端点,将每个端点映射到一个特定的URL,并(可选地)启用和配置SockJS回退选项。
     *
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册websocket站点、允许跨域、允许不支持ws端使用SockJs。http://localhost:8080/ws
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

    /**
     * 配置与处理从WebSocket客户端接收和发送到WebSocket客户端的消息相关的选项。
     *
     * @param registry
     */
    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
    }

    /**
     * 配置{@link org.springframework.messaging.MessageChannel}用于从WebSocket客户端传入消息。
     * 默认情况下,通道由大小为1的线程池支持。建议为生产使用定制线程池设置。
     *
     * @param registration
     */
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
    }

    /**
     * 配置{@link org.springframework.messaging.MessageChannel}用于向WebSocket客户端出站消息。
     * 默认情况下,通道由大小为1的线程池支持。建议为生产使用定制线程池设置。
     *
     * @param registration
     */
    @Override
    public void configureClientOutboundChannel(ChannelRegistration registration) {

    }

    /**
     * 添加解析器以支持自定义控制器方法参数类型。这并不覆盖解析处理程序方法参数的内置支持。
     * 要自定义参数解析的内置支持,请直接配置{@code SimpAnnotationMethodMessageHandler}。
     *
     * @param argumentResolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {

    }

    /**
     * 添加处理程序以支持自定义控制器方法返回值类型。<p>使用此选项不会覆盖处理返回值的内置支持。
     * 要自定义处理返回值的内置支持,请直接配置{@code SimpAnnotationMethodMessageHandler}。
     *
     * @param returnValueHandlers
     */
    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {

    }

    /**
     * 配置消息转换器,以便在提取带注释的方法中的消息有效负载和发送消息时使用(例如通过“代理”SimpMessagingTemplate)。
     * 提供的列表(最初为空)可用于添加消息转换器,而布尔返回值用于确定是否也应添加默认消息。
     *
     * @param messageConverters
     * @return
     */
    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
        return false;
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //全局使用的消息前缀
        registry.setApplicationDestinationPrefixes("/");
        //订阅broker的名称
        registry.enableSimpleBroker("/chatRoom");
    }
}

MessageController

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Controller
public class MessageController {
    private static final String ONLINE_USERS = "onlineUsers";
    private static List<String> list = new LinkedList<>();

    @MessageMapping("/sendMessage")
    @SendTo("/chatRoom")
    public Message sendMessage(@RequestBody Message message) {
        return message;
    }

    @MessageMapping("/addUser")
    @SendTo("/chatRoom")
    public Message addUser(@RequestBody Message message, SimpMessageHeaderAccessor headerAccessor) {
        //添加到全局Map里面
        list.add(message.getUsername());
        System.out.println(list.size());
        return message;
    }

    @MessageMapping("/delUser")
    @SendTo("/chatRoom")
    public Message delUser(@RequestBody Message message) {
        //删除用户
        list.remove(message.getUsername());
        System.out.println(list.size());
        return message;
    }

    @MessageMapping("/getUsers")
    @SendTo("/chatRoom")
    public Integer getUsers() {
        //获取在线的人数
        return list.size();
    }
}

WebSocketEventListener

java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class WebSocketEventListener {
    private static final Logger log = LoggerFactory.getLogger(WebSocketEventListener.class);

    @Autowired
    private SimpMessageSendingOperations simpMessageTemplate;

    @EventListener
    public void connect(SessionConnectEvent event) {
        log.info("正在连接...");
    }

    @EventListener
    public void connected(SessionConnectedEvent event) {
        log.info("已连接上!");
    }

    @EventListener
    public void disconnected(SessionDisconnectEvent event) {
        log.info("关闭连接!");
    }
}

运行结果,还存在一点问题,需要进行修改。

2.png