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


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

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

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

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

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

相关推荐
Wokoo738 分钟前
WebSocket :实时通信技术对比
网络·websocket·网络协议·http·信息与通信
galaxyffang39 分钟前
WebSocket 和 Http 的核心区别
websocket·网络协议·计算机网络·http
百万蹄蹄向前冲6 小时前
Trae Genimi3跟着官网学实时通信 Socket.io框架
前端·后端·websocket
x***38169 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
S***84889 小时前
SpringSecurity踢出指定用户
java
p***s919 小时前
Spring数据库原理 之 DataSource
java·数据库·spring
adobehu9 小时前
麒麟系统安装jdk17
java·jdk
spencer_tseng9 小时前
java.util.IllegalFormatPrecisionException
java·printf
虹科网络安全9 小时前
艾体宝干货 | Redis Java 开发系列#1 从零开始的环境搭建与实践指南
java·数据库·redis