WebSocket

背景

在实现聊天室项目的时候,我遇到了一个问题,那就是消息是如何从服务器端自己推送给客户端的呢?一开始我的想法是每隔一段时间,就让客户端向服务器端请求数据,也就是所谓的"轮询"机制,但这种方式显然是太耗费资源的,我在查阅资料后,发现有一个应用层协议专门服务于这种情况的:WebSocket。 (依旧是基于传输层的TCP实现的)

报文格式

websocket握手过程

代码示例

先来编写服务器端的代码:

1)创建一个类,作为WebSocketHandler(处理websocket中的各个通信流程)

具体的业务场景:

java 复制代码
@Component
public class WebSocketComponent extends TextWebSocketHandler {

    @Autowired
    OnlineUserManager onlineUserManager;
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    MessageMapper messageMapper;

    @Autowired
    MessageSessionMapper messageSessionMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("SESSION_USER");
        if(userInfo == null){
            return;
        }
        System.out.println("get websocket session -> "+userInfo.getId());
        onlineUserManager.online(userInfo.getId(),session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("SESSION_USER");
        if(userInfo == null){
            return;
        }

        MessageRequest request = objectMapper.readValue(message.getPayload(),MessageRequest.class);
        if(request.getType().equals("message")){
            transferMessage(userInfo,request);
        }else{
            System.out.println("ERROR : MessageRequest:Type is error!");
        }
    }

    private void transferMessage(UserInfo userInfo, MessageRequest request) throws IOException {
        //先构造一个response 并且转成String类型
        MessageResponse messageResponse = new MessageResponse();
        messageResponse.setContent(request.getContent());
        messageResponse.setFromid(userInfo.getId());
        messageResponse.setFromname(userInfo.getUsername());
        messageResponse.setSessionid(request.getSessionId());
        String resJson = objectMapper.writeValueAsString(messageResponse);
        //通过sessionId 来获取好友列表 再遍历这个链表 进行传输信息
        List<Friend> list = messageSessionMapper.getFriendsBySID(request.getSessionId(),userInfo.getId());
        Friend mySelf = new Friend();
        mySelf.setFriendname(userInfo.getUsername());
        mySelf.setFriendId(userInfo.getId());
        list.add(mySelf);
        for(Friend friend : list){
            WebSocketSession webSocketSession = onlineUserManager.getSession(friend.getFriendId());
            if(webSocketSession == null){
                // 这种情况是未登录的状态 先不发送
                continue;
            }else{
                webSocketSession.sendMessage(new TextMessage(resJson));
            }
        }
        // 还要把这个消息 发给 数据库 进行永久保存操作
        Message message = new Message();
        message.setFromid(userInfo.getId());
        message.setSessionid(request.getSessionId());
        message.setContent(request.getContent());
        messageMapper.add(message);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("SESSION_USER");
        if(userInfo == null){
            return;
        }
        onlineUserManager.offline(userInfo.getId(),session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        UserInfo userInfo = (UserInfo) session.getAttributes().get("SESSION_USER");
        if(userInfo == null){
            return;
        }
        onlineUserManager.offline(userInfo.getId(),session);
    }
}

2)把上述类的实例,给注册到Spring里面,配置路由。(关联上哪个路径对应到上述的handler)

java 复制代码
@Configuration
@EnableWebSocket    //启动websocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    WebSocketComponent webSocketComponent;

    //通过这个方法 进行路由注册
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketComponent,"websocketmessage")
                .addInterceptors(new HttpSessionHandshakeInterceptor());
    }
}

写客户端的代码:

相关推荐
記億揺晃着的那天3 小时前
NAS 内网域名访问为什么需要浏览器授权
网络·nginx·js·nas
chunmiao30323 小时前
DeepSeek V4-Pro 正式版上线,API 8月17日起分时调价
网络·安全
迪康Defender6 小时前
AI 重构终端安全运营:智能分析中枢 AI Insight 模块架构与落地场景深度解析
运维·网络·人工智能·其他·安全·重构·架构
0x536 小时前
Java网络编程(二)
java·服务器·网络
AI领路人.6 小时前
[特殊字符] 用 OpenClaw 实现一个 C++ 复刻版 QuantClaw
websocket·单元测试·discord·rest api·内存占用·c++17
微硬创新6 小时前
老旧产线改造:耐达讯自动化16路4‑20mA转PROFINET的工程实践
人工智能·网络协议·自动化·信息与通信
西安景驰电子8 小时前
《PTP精确时间协议系列》第一篇:从原理到应用,全面解读IEEE 1588
linux·运维·服务器·开发语言·网络·windows·php
ZGi.ai8 小时前
多模型回答不稳定:路由规则与评测方法
网络·人工智能·大模型评测·多模型·模型路由·zgi·模型网关
80s7779 小时前
独享住宅IP、长效代理ip是什么?有哪些作用?
网络·网络协议·tcp/ip
IPdodo_9 小时前
静态 IP、住宅 IP 和固定出口有什么区别?开发场景选型指南
服务器·网络·网络协议·tcp/ip·代理ip