WebSocket实战:让服务器和客户端“煲电话粥”

大家好,我是小悟。

一、WebSocket是啥?🤔

想象一下这个场景:你(客户端)和服务器是一对异地恋情侣📞

传统HTTP通信(像发短信)

  • 你:"在干嘛?"(发送请求)
  • 等待...(服务器处理)
  • 服务器:"在吃饭"(返回响应)
  • 聊天结束,连接断开
  • 10秒后你又问:"吃完没?"(重新建立连接)
  • 重复以上步骤...累不累?流量费不要钱啊!

WebSocket通信(像打电话)

  • 你:"喂,能听到吗?"(握手)
  • 服务器:"听到啦,随时聊!"(握手成功)
  • 然后你们可以:
    • 你:"今天天气不错"
    • 服务器:"是啊,适合出去玩"
    • 服务器:"对了,你快递到了"
    • 你:"太好了!"
  • 想聊多久聊多久,不用挂断!💑

简单说,WebSocket就是那个不用反复拨号、永远在线的爱情热线!而且它是双向的------服务器可以主动撩你,不用你总是先开口。

二、详细步骤:手把手教你"煲电话粥"📱

第1步:加依赖(准备电话机)

复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

第2步:配置WebSocket(安装电话总机)

复制代码
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/chat")
                .setAllowedOrigins("*"); // 允许所有异地恋(生产环境别这么干!)
    }
    
    @Bean
    public WebSocketHandler myHandler() {
        return new MyWebSocketHandler();
    }
}

第3步:实现处理器(训练话务员)

复制代码
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {
    
    // 用户列表(记住谁在打电话)
    private static final Map<String, WebSocketSession> users = new ConcurrentHashMap<>();
    
    /**
     * 接通电话后的第一句
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String userId = getUserId(session);
        users.put(userId, session);
        
        System.out.println("👋 用户 " + userId + " 上线了!");
        sendMessage(session, "系统:欢迎来到聊天室!输入 'bye' 可以挂断哦~");
        
        // 广播有人上线
        broadcast("系统:用户 " + userId + " 悄悄潜入聊天室...");
    }
    
    /**
     * 处理对方说的话
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String userId = getUserId(session);
        String payload = message.getPayload();
        
        System.out.println("📩 收到来自 " + userId + " 的消息: " + payload);
        
        if ("bye".equalsIgnoreCase(payload)) {
            // 说再见就挂电话
            session.close();
            return;
        }
        
        // 广播消息(让所有人都听到)
        String formattedMsg = userId + " 说: " + payload;
        broadcast(formattedMsg);
    }
    
    /**
     * 电话出问题了(信号不好)
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        System.out.println("❌ 信号不好,用户 " + getUserId(session) + " 掉线了");
        users.remove(getUserId(session));
    }
    
    /**
     * 电话挂了(正常挂断)
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String userId = getUserId(session);
        users.remove(userId);
        System.out.println("👋 用户 " + userId + " 下线了");
        broadcast("系统:用户 " + userId + " 挥一挥衣袖,带走了所有云彩~");
    }
    
    /**
     * 群发消息(广播)
     */
    private void broadcast(String message) {
        users.forEach((id, session) -> {
            if (session.isOpen()) {
                sendMessage(session, message);
            }
        });
    }
    
    /**
     * 发送消息(确保安全)
     */
    private void sendMessage(WebSocketSession session, String message) {
        try {
            session.sendMessage(new TextMessage(message));
        } catch (IOException e) {
            System.out.println("💥 发送消息失败: " + e.getMessage());
        }
    }
    
    /**
     * 从session里提取用户ID(实际项目从token取)
     */
    private String getUserId(WebSocketSession session) {
        // 这里简化处理,实际应该从认证信息获取
        return session.getId().substring(0, 8);
    }
}

第4步:前端客户端(你的手机)

复制代码
<!DOCTYPE html>
<html>
<head>
    <title>WebSocket聊天室</title>
    <style>
        body { font-family: Arial; max-width: 600px; margin: 0 auto; padding: 20px; }
        #messages { border: 1px solid #ccc; height: 300px; overflow-y: auto; padding: 10px; margin-bottom: 10px; }
        .message { margin: 5px 0; padding: 8px; border-radius: 5px; }
        .system { background-color: #f0f0f0; color: #666; }
        .user { background-color: #e3f2fd; }
    </style>
</head>
<body>
    <h2>📱 WebSocket聊天室</h2>
    <div id="messages"></div>
    <input type="text" id="messageInput" placeholder="输入消息...">
    <button onclick="sendMessage()">发送</button>
    <button onclick="disconnect()" style="color: red;">挂断电话</button>

    <script>
        let socket = null;
        
        // 自动连接
        window.onload = function() {
            connect();
        };
        
        function connect() {
            // 换成你的服务器地址
            socket = new WebSocket('ws://localhost:8080/chat');
            
            socket.onopen = function() {
                addMessage('系统', '电话接通啦!开始聊天吧~', 'system');
            };
            
            socket.onmessage = function(event) {
                const msg = event.data;
                if (msg.startsWith('系统:')) {
                    addMessage('系统', msg.substring(3), 'system');
                } else if (msg.includes('说:')) {
                    const parts = msg.split('说:');
                    addMessage(parts[0], parts[1], 'user');
                }
            };
            
            socket.onclose = function() {
                addMessage('系统', '电话已挂断,点击发送可重连', 'system');
            };
            
            socket.onerror = function() {
                addMessage('系统', '信号不好,请检查网络', 'system');
            };
        }
        
        function sendMessage() {
            const input = document.getElementById('messageInput');
            const message = input.value.trim();
            
            if (!socket || socket.readyState !== WebSocket.OPEN) {
                connect();
                setTimeout(() => sendMessage(), 500);
                return;
            }
            
            if (message) {
                socket.send(message);
                input.value = '';
            }
        }
        
        function disconnect() {
            if (socket) {
                socket.send('bye');
                socket.close();
            }
        }
        
        function addMessage(sender, content, type) {
            const messagesDiv = document.getElementById('messages');
            const msgDiv = document.createElement('div');
            msgDiv.className = `message ${type}`;
            msgDiv.innerHTML = `<strong>${sender}:</strong> ${content}`;
            messagesDiv.appendChild(msgDiv);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }
        
        // 回车发送
        document.getElementById('messageInput').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>

第5步:进阶功能(VIP服务)

复制代码
// 1. 心跳检测(防止假死)
@Component
public class HeartbeatScheduler {
    @Scheduled(fixedRate = 30000) // 每30秒
    public void checkAlive() {
        MyWebSocketHandler.getUsers().forEach((id, session) -> {
            try {
                session.sendMessage(new TextMessage("ping"));
            } catch (IOException e) {
                // 用户可能已经溜了
            }
        });
    }
}

// 2. 私聊功能(悄悄话)
public void sendPrivateMessage(String fromUserId, String toUserId, String message) {
    WebSocketSession targetSession = users.get(toUserId);
    if (targetSession != null && targetSession.isOpen()) {
        sendMessage(targetSession, "【私聊】" + fromUserId + " 对你说: " + message);
    }
}

// 3. 在线用户列表
public List<String> getOnlineUsers() {
    return new ArrayList<>(users.keySet());
}

三、总结:WebSocket的"爱情哲学"💕

WebSocket的优势:

  1. 省流量:一次握手,长久通话(HTTP每次都要说"喂,能听到吗?")
  2. 实时性:服务器可以主动"撩"客户端(推送消息)
  3. 低延迟:不用反复建立连接(像永远不挂的电话)

适用场景:

  • ✅ 聊天应用(微信、钉钉)
  • ✅ 实时游戏(王者荣耀的队友位置)
  • ✅ 股票行情(股价跳动)
  • ✅ 协同编辑(腾讯文档)
  • ✅ 物联网监控(设备状态实时更新)

注意事项:

  1. 连接管理:用户多了要小心,别把服务器压垮
  2. 心跳机制:定期说句话,确认对方还在线
  3. 重连策略:网络断了要自动重拨
  4. 安全性:别让陌生人随便"打电话"进来
  5. 协议兼容:有些老浏览器不支持WebSocket

最后:

为什么WebSocket比HTTP更适合聊天?

因为HTTP每次聊天前都要问:"在吗?"

等收到"在"的时候,你已经不想聊了...😂

WebSocket就像一段好的感情------不需要反复确认,始终彼此连接,随时可以说话。用它来构建实时应用,让数据像对话一样自然流动吧!


实战建议:从小聊天室开始,慢慢添加房间管理、消息历史、文件传输等功能。记住,能力越大,责任越大------实时应用很酷,但也需要考虑性能、扩展性和安全性哦!

谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。

您的一键三连,是我更新的最大动力,谢谢

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

相关推荐
num_killer16 分钟前
小白的Langchain学习
java·python·学习·langchain
期待のcode1 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐1 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲1 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红1 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥1 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v1 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地2 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209252 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei2 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot