Angular与非Spring Java后端双向WebSocket通信问题排查
哦,我明白你的问题了——你现在遇到的核心问题是协议不兼容:Angular端用的是Socket.IO客户端,但Java后端是原生的TCP Socket服务器,这俩根本不是一个路子!Socket.IO不是简单的原生TCP,它有自己的握手流程、帧格式和传输机制(默认会先尝试轮询再升级到WebSocket),所以直接连原生Socket肯定收不到预期的数据。
下面给你两种可行的解决方案,你可以根据需求选择:
方案一:把Java后端改成Socket.IO服务器(推荐,适配现有Angular客户端)
这个方案让前后端使用同一种Socket.IO协议,能无缝实现双向通信。我们可以用官方的Java Socket.IO库来改造后端。
步骤1:给Java项目添加依赖
如果用Maven,在pom.xml中加入以下依赖:
<dependency> <groupId>io.socket</groupId> <artifactId>socket.io-server</artifactId> <version>4.6.1</version> </dependency> <dependency> <groupId>io.socket</groupId> <artifactId>socket.io-client</artifactId> <version>4.6.1</version> </dependency>
步骤2:编写Socket.IO服务器代码
替换你原来的原生Socket服务器,改成如下实现:
import io.socket.server.SocketIoServer; import io.socket.socketio.server.SocketIoNamespace; import io.socket.socketio.server.SocketIoSocket; public class SocketIoServerExample { public static void main(String[] args) { // 启动Socket.IO服务器,监听5000端口 SocketIoServer server = new SocketIoServer(5000); // 获取默认命名空间(与Angular客户端连接路径对应) SocketIoNamespace namespace = server.namespace("/"); // 监听客户端连接事件 namespace.on("connection", (args) -> { SocketIoSocket socket = (SocketIoSocket) args[0]; System.out.println("客户端已连接: " + socket.getId()); // 监听前端发送的"move"事件(与Angular中emit的事件名一致) socket.on("move", (eventArgs) -> { String action = (String) eventArgs[0]; System.out.println("收到客户端动作: " + action); // 给前端发送回应,事件名可自定义 socket.emit("message-backend", "已收到你的动作: " + action); }); // 监听客户端断开连接事件 socket.on("disconnect", (disconnectArgs) -> { System.out.println("客户端已断开: " + socket.getId()); }); }); // 启动服务器 server.start(); System.out.println("Socket.IO服务器已启动,监听端口5000"); } }
步骤3:调整Angular客户端代码
你的Angular代码基础没问题,只需补充监听后端回应的逻辑,并去掉@ts-ignore(先安装类型定义):
首先安装依赖:
npm install socket.io-client npm install --save-dev @types/socket.io-client
然后修改组件代码:
import {AfterViewInit, Component, OnInit} from '@angular/core'; import { io, Socket } from 'socket.io-client'; @Component({ selector: 'app-server-component', templateUrl: './server-component.component.html', styleUrls: ['./server-component.component.scss'] }) export class ServerComponentComponent implements OnInit, AfterViewInit { private socket: Socket; public ngOnInit() { // 连接Socket.IO服务器,指定使用websocket传输(减少轮询) this.socket = io("http://localhost:5000", { transports: ["websocket"] }); // 监听后端发送的"message-backend"事件,用于更新UI this.socket.on("message-backend", (response) => { console.log("后端回应:", response); // 这里可以将response绑定到模板,实现实时文本展示 }); // 监听连接成功事件 this.socket.on("connect", () => { console.log("已连接到服务器"); }); } public ngAfterViewInit() { } public move(action: string) { this.socket.emit("move", action); console.log("发送动作:", action); } }
方案二:Angular改用原生WebSocket,Java后端对应实现WebSocket服务
如果你不想用Socket.IO,也可以用浏览器原生支持的WebSocket协议,Java后端实现对应的WebSocket服务即可。
步骤1:Java后端实现WebSocket服务器
使用JSR-356标准API(需要部署在Tomcat、Jetty等Servlet容器,或使用嵌入式容器):
import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; @ServerEndpoint("/ws") public class WebSocketServer { @OnOpen public void onOpen(Session session) { System.out.println("客户端已连接: " + session.getId()); } @OnMessage public void onMessage(String message, Session session) throws IOException { System.out.println("收到客户端消息: " + message); // 给客户端发送回应 session.getBasicRemote().sendText("已收到消息: " + message); } @OnClose public void onClose(Session session) { System.out.println("客户端已断开: " + session.getId()); } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } }
步骤2:Angular客户端使用原生WebSocket
import {AfterViewInit, Component, OnInit} from '@angular/core'; @Component({ selector: 'app-server-component', templateUrl: './server-component.component.html', styleUrls: ['./server-component.component.scss'] }) export class ServerComponentComponent implements OnInit, AfterViewInit { private websocket: WebSocket; public ngOnInit() { // 连接WebSocket服务器(注意端口为容器端口,比如Tomcat默认8080) this.websocket = new WebSocket("ws://localhost:8080/ws"); this.websocket.onopen = () => { console.log("WebSocket连接已建立"); }; this.websocket.onmessage = (event) => { console.log("后端消息:", event.data); // 更新UI展示实时文本 }; this.websocket.onerror = (error) => { console.error("WebSocket错误:", error); }; this.websocket.onclose = () => { console.log("WebSocket连接已关闭"); }; } public move(action: string) { // 确保连接已打开再发送数据 if (this.websocket.readyState === WebSocket.OPEN) { this.websocket.send(action); console.log("发送动作:", action); } else { console.error("WebSocket未连接,请稍后重试"); } } }
为什么原来的代码无法工作?
你之前的Java后端是原生TCP Socket,它只会接收原始字节流,但Angular的Socket.IO客户端发送的是符合Socket.IO协议格式的封装数据(包含事件名、数据等),而且Socket.IO会先发送HTTP请求进行握手,所以后端收到的是GET /socket.io/?EIO=3&transport=polling...这类请求,根本不是你预期的action文本。
选择任意一种方案改造后,就能实现两端的双向实时通信了!
内容的提问来源于stack exchange,提问作者divya jain

