Angular与Spring Boot Socket.IO通信异常问题求助
Angular与Spring Boot Socket.IO通信异常排查与解决
问题现象
- Spring Boot日志显示Angular端频繁触发连接、断开事件
- Angular控制台报错
Socket is not connected, cannot send message! - 浏览器Socket连接请求URL未携带
room参数 - 对接Node Mock服务器时通信正常,仅Spring Boot端出现问题
现有代码
Angular服务代码
@Injectable({ providedIn: 'root' }) export class WebsocketService { private socket: any; isBrowser = false; constructor(@Inject(PLATFORM_ID) private platformId: Object) { this.isBrowser = isPlatformBrowser(this.platformId) if (this.isBrowser) { console.log("hello") this.initSocket(); } } private initSocket(): void { this.socket = io('http://ipv4:8081'); this.socket.on('connect', () => { console.log('Connected to Socket.IO server'); }); this.socket.on('disconnect', () => { console.log('Disconnected from Socket.IO server'); }); this.socket.on('get_message', (data: any) => { // Handle incoming messages from the server console.log('Received message:', data); // You can emit events or update your application state here }); } sendMessage(room: string, message: any): void { if (this.isBrowser) { this.initSocket(); if (this.socket.connected) { this.socket.emit('send_message', { room, message }); } else { console.error('Socket is not connected, cannot send message!'); } } } }
Spring Boot应用代码
@Slf4j @Component public class SocketIOModule { private final SocketIOServer server; private final SocketIOService socketService; public SocketIOModule(SocketIOServer server, SocketIOService socketService) { this.server = server; this.socketService = socketService; this.server.addConnectListener(onConnected()); this.server.addDisconnectListener(onDisconnected()); this.server.addEventListener("send_message", Message.class, onChatReceived()); } private DataListener<Message> onChatReceived() { return (senderClient, data, ackSender) -> { log.info("Data: {}",data.toString()); socketService.sendMessage(data.getRoom(),"get_message", senderClient, data.getMessage()); }; } private ConnectListener onConnected() { return (client) -> { log.info(String.valueOf(client.getHandshakeData())); final String roomVariable = client.getHandshakeData().getSingleUrlParam("room"); final String room = Objects.nonNull(roomVariable)?roomVariable:"temp"; log.info("Room: {}",room); client.joinRoom(room); log.info("Socket ID[{}] Connected to socket", client.getSessionId().toString()); }; } private DisconnectListener onDisconnected() { return client -> { log.info("Client[{}] - Disconnected from socket", client.getSessionId().toString()); }; } }
问题根源与修复方案
1. 重复初始化Socket导致频繁断开
Angular的sendMessage方法每次调用都会执行initSocket(),创建新的Socket连接,旧连接被强制断开,引发服务器端频繁的连接/断开日志。
修复代码:
@Injectable({ providedIn: 'root' }) export class WebsocketService { private socket: any; isBrowser = false; constructor(@Inject(PLATFORM_ID) private platformId: Object) { this.isBrowser = isPlatformBrowser(this.platformId); if (this.isBrowser) { this.initSocket(); } } private initSocket(room?: string): void { // 避免重复初始化已连接的Socket if (this.socket?.connected) return; // 携带room参数建立连接 const queryParams = room ? { room } : {}; this.socket = io('http://ipv4:8081', { query: queryParams }); this.bindSocketListeners(); } // 抽离监听逻辑,避免重复绑定事件 private bindSocketListeners(): void { this.socket.on('connect', () => { console.log('Connected to Socket.IO server'); }); this.socket.on('disconnect', () => { console.log('Disconnected from Socket.IO server'); }); this.socket.on('get_message', (data: any) => { console.log('Received message:', data); }); } sendMessage(room: string, message: any): void { if (!this.isBrowser) return; // 检查Socket状态,未连接则携带当前room重建连接 if (!this.socket?.connected) { this.initSocket(room); // 延迟发送,确保连接建立完成 setTimeout(() => { this.socket.emit('send_message', { room, message }); }, 500); } else { this.socket.emit('send_message', { room, message }); } } }
2. 连接时未携带room参数
Spring Boot端在连接阶段通过URL参数获取room,但原Angular代码初始化Socket时未传递该参数,导致服务器端使用默认的temp房间,同时可能因参数缺失引发连接兼容性问题。
修复:
在initSocket方法中通过query参数传递room信息,如上述修复代码所示,确保连接请求URL携带room参数。
3. Socket.IO版本兼容性问题
Node的Socket.IO服务器与Spring Boot使用的netty-socketio版本可能不匹配,Angular端socket.io-client版本需与服务端版本兼容。
检查与修复:
- 确认Angular端
socket.io-client版本与Spring Boot端netty-socketio版本一致(例如客户端用4.x,服务端也需对应4.x系列) - Spring Boot Maven依赖示例:
<dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>2.0.11</version> <!-- 需与客户端版本匹配 --> </dependency>
4. 跨域配置缺失
Spring Boot端未配置正确的跨域规则,导致浏览器拦截Socket连接请求,引发频繁断开。
修复代码:
@Configuration public class SocketIOConfig { @Bean public SocketIOServer socketIOServer() { Configuration config = new Configuration(); config.setHostname("0.0.0.0"); config.setPort(8081); // 允许指定域名跨域,生产环境请勿用* config.setOrigin("http://your-angular-domain:port"); return new SocketIOServer(config); } }
内容的提问来源于stack exchange,提问作者kaguya shinomiya
相关产品推荐
相关产品推荐

