Golang WebSocket客户端连接NestJS网关失败求可用示例
解决Golang客户端连接NestJS WebSocket网关失败的问题
问题现象
连接NestJS网关WebSocket服务器时,Golang客户端使用gorilla/websocket库出现错误:
2024/06/27 23:33:48 Error connecting to WebSocket: unexpected EOFPostman可以正常连接,但Golang客户端始终无法成功建立连接。
问题原因
NestJS默认使用SocketIO作为WebSocket适配器,而gorilla/websocket是标准WebSocket协议的客户端,不兼容SocketIO的自定义握手协议。你的NestJS代码虽导入了ws库,但未配置使用WsAdapter,因此网关实际运行在SocketIO模式下,导致标准WebSocket客户端连接失败。
解决方案
1. 修改NestJS网关,启用标准WebSocket协议
需要配置WsAdapter让NestJS使用标准WebSocket协议,并修正ws库的API调用:
import { SubscribeMessage, WebSocketGateway, WebSocketServer, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, WsAdapter, } from '@nestjs/websockets'; import { Server, WebSocket } from 'ws'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; // 替换为你的实际模块路径 // 应用启动时配置WsAdapter async function bootstrap() { const app = await NestFactory.create(AppModule); app.useWebSocketAdapter(new WsAdapter(app)); await app.listen(3000); } bootstrap(); @WebSocketGateway() export class SockServerGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; private clients: Map<string, WebSocket> = new Map(); afterInit(server: Server) { console.log('WebSocket server initialized'); } handleConnection(client: WebSocket) { // ws库实例默认无id属性,手动生成唯一ID const clientId = Math.random().toString(36).substring(2, 15); (client as any).id = clientId; this.clients.set(clientId, client); console.log(`Client connected: ${clientId}`); this.displayClients(); } handleDisconnect(client: WebSocket) { const clientId = (client as any).id; this.clients.delete(clientId); console.log(`Client disconnected: ${clientId}`); this.displayClients(); } @SubscribeMessage('message') handleMessage(client: WebSocket, payload: any): void { const clientId = (client as any).id; const receivedData = payload.data; // 解析客户端发送的JSON数据 console.log(`Received message from ${clientId}: ${receivedData}`); // ws库使用send方法发送JSON格式消息,而非emit client.send(JSON.stringify({ event: 'message', data: 'xxx' })); // 广播消息给所有在线客户端 this.server.clients.forEach((ws) => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ event: 'message', data: `Message from ${clientId}: ${receivedData}` })); } }); } private displayClients() { console.log('Connected clients:'); this.clients.forEach((_, id) => { console.log(id); }); } sendMessageToClient(clientId: string, message: string): void { const client = this.clients.get(clientId); if (client && client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ event: 'message', data: message })); } else { console.log(`Client with ID ${clientId} not found or disconnected`); } } sendMessageToAllClients(message: string): void { this.server.clients.forEach((ws) => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ event: 'message', data: message })); } }); } }
2. 适配的Golang客户端代码
以下是可以成功连接上述NestJS网关的Golang客户端示例:
package main import ( "encoding/json" "fmt" "log" "os" "os/signal" "time" "github.com/gorilla/websocket" ) // 定义与NestJS匹配的消息结构体 type SocketMessage struct { Event string `json:"event"` Data string `json:"data"` } func main() { url := "ws://localhost:3000" // 配置Dialer,添加Origin头避免跨域拦截 dialer := websocket.DefaultDialer dialer.Subprotocols = []string{"ws"} conn, _, err := dialer.Dial(url, map[string][]string{ "Origin": {"http://localhost:3000"}, }) if err != nil { log.Fatalf("连接WebSocket失败: %v", err) } defer conn.Close() // 启动协程处理接收消息 go func() { defer conn.Close() for { _, msgBytes, err := conn.ReadMessage() if err != nil { log.Printf("读取消息失败: %v", err) return } var msg SocketMessage if err := json.Unmarshal(msgBytes, &msg); err != nil { log.Printf("解析消息失败: %v", err) continue } fmt.Printf("收到[%s]: %s\n", msg.Event, msg.Data) } }() // 定时发送消息 ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() done := make(chan os.Signal, 1) signal.Notify(done, os.Interrupt) for { select { case <-done: fmt.Println("正在退出...") // 发送优雅关闭帧 err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { log.Printf("发送关闭消息失败: %v", err) } return case <-ticker.C: // 构造符合网关要求的消息格式 sendMsg := SocketMessage{ Event: "message", Data: "Hello from Go!", } msgBytes, err := json.Marshal(sendMsg) if err != nil { log.Printf("序列化消息失败: %v", err) continue } err = conn.WriteMessage(websocket.TextMessage, msgBytes) if err != nil { log.Printf("发送消息失败: %v", err) return } fmt.Printf("发送[%s]: %s\n", sendMsg.Event, sendMsg.Data) } } }
关键说明
- NestJS端:必须配置
WsAdapter才能支持标准WebSocket协议,同时使用ws库的send方法替代SocketIO的emit方法,消息需封装为JSON格式。 - Golang端:需发送包含
event字段的JSON消息,匹配NestJS的@SubscribeMessage监听;添加Origin头避免跨域拦截。
内容的提问来源于stack exchange,提问作者EngVivo
相关产品推荐
相关产品推荐

