ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Godot WebSocket Chat 实战:用 GDScript 在单个工程内同时托管 WebSocket 服务器与客户端

Godot WebSocket Chat 实战:用 GDScript 在单个工程内同时托管 WebSocket 服务器与客户端 示例工程【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址https://gitcode.com/GitHub_Trending/go/godot-demo-projects点击查看免费下载这是一篇基于 godot-demo-projects 仓库中networking/websocket_chat演示项目的技术指南。该演示展示了一个用 WebSockets 实现的简易聊天应用重点说明两件事如何在 Godot 中托管host一个 WebSocket 服务器以及如何从 Godot 中作为客户端连接它。读完本文你将掌握WebSocketPeer、TCPServer的配合用法、服务端多客户端管理、协议协商subprotocol、以及如何用信号驱动的方式组织一套可复用的 WebSocket 封装脚本。项目概览一个工程两种角色networking/websocket_chat是 godot-demo-projects 仓库 networking 目录下的官方演示项目工程名称为 WebSocket Chat Demo见 project.godot。它最值得称道的设计是同一个进程里既可以启动服务器也可以同时运行多个客户端通过组合场景combo scene把两者拼在一起打开工程就能直观看到完整的聊天收发过程。项目基础信息来自 README.md语言GDScript渲染器Compatibility兼容模式对应 project.godot 中的renderer/rendering_methodgl_compatibility主场景combo.tscn对应 project.godot 的run/main_scene最低引擎版本Godot 4.7见config/featuresPackedStringArray(4.7)工程结构先分清四个脚本的职责整个演示只有两个核心文件目录级资源一个共享 UI 场景和四个 GDScript 文件结构非常清晰networking/websocket_chat/ ├── project.godot # 工程配置 ├── combo.tscn # 主场景服务器 3 个客户端并排 ├── chat.tscn # 通用聊天 UI 面板 ├── client.tscn / server.tscn # 客户端/服务器实例化场景 ├── client.gd # 客户端 UI 逻辑 ├── server.gd # 服务器 UI 逻辑 └── websocket/ ├── WebSocketClient.gd # 可复用的客户端封装Node 子类 └── WebSocketServer.gd # 可复用的服务器封装Node 子类其中websocket/下的两个脚本是演示的核心资产它们以class_name声明为全局类型WebSocketClient、WebSocketServer把 Godot 底层的WebSocketPeer和TCPServer封装成带信号、可配置的Node可以直接拖进你自己的项目复用。client.gd与server.gd只是把封装节点上的信号接到 UI 上做展示。场景设计共享 UI 实例化拆分chat.tscn公用的聊天面板chat.tscn 是一个纯 UI 场景包含顶部Connect行Host地址输入框默认ws://localhost:8000/test/、Connect按钮toggle 模式、Port端口 SpinBox1~65535默认 8000、Listen按钮toggle 模式中部Send行消息输入LineEdit和Send按钮底部一个RichTextLabel作为聊天日志显示区。这个 UI 同时服务两种角色靠子场景覆盖配置区分server.tscn 实例化chat.tscn后挂上server.gd与WebSocketServer节点并隐藏Host输入框和Connect按钮visible false只保留Port与Listenclient.tscn 实例化chat.tscn后挂上client.gd与WebSocketClient节点并隐藏Port与Listen控件只保留Host和Connect。combo.tscn一屏演示完整收发combo.tscn 是主场景用HBoxContainer把1 个服务器实例 3 个客户端实例Client、Client2、Client3并排摆开。运行后你可以点服务器侧Listen开启监听再点任意客户端的Connect连到ws://localhost:8000/test/随后任何一个客户端发消息服务器会广播给所有客户端效果一目了然。客户端封装WebSocketClient 源码逐段解析WebSocketClient.gd 是一个继承Node的全局类核心是持有一个WebSocketPeer实例class_name WebSocketClient extends Node export var handshake_headers: PackedStringArray export var supported_protocols: PackedStringArray var tls_options: TLSOptions null var socket : WebSocketPeer.new() var last_state : WebSocketPeer.STATE_CLOSED signal connected_to_server() signal connection_closed() signal message_received(message: Variant)三个导出变量对应 WebSocket 握手的关键参数supported_protocols客户端声明的子协议列表握手时发送给服务器服务器可从其中选择一个作为最终协议handshake_headers额外的 HTTP 握手请求头如鉴权 tokentls_optionsTLS 选项用于wss://加密连接。连接与发送func connect_to_url(url: String) - int: socket.supported_protocols supported_protocols socket.handshake_headers handshake_headers var err : socket.connect_to_url(url, tls_options) if err ! OK: return err last_state socket.get_ready_state() return OKconnect_to_url把导出配置写入底层WebSocketPeer后发起连接返回错误码非OK表示失败。send则按消息类型分流字符串走send_text否则用var_to_bytes做序列化后发送——这意味着该封装天然支持发送任意 Variant 类型的数据func send(message: String) - int: if typeof(message) TYPE_STRING: return socket.send_text(message) return socket.send(var_to_bytes(message))poll把状态机翻译成信号这是封装最精妙的地方。poll()在每个_process帧被调用把底层WebSocketPeer的原始状态迁移翻译成高层的三个信号func poll() - void: if socket.get_ready_state() ! socket.STATE_CLOSED: socket.poll() var state : socket.get_ready_state() if last_state ! state: last_state state if state socket.STATE_OPEN: connected_to_server.emit() elif state socket.STATE_CLOSED: connection_closed.emit() while socket.get_ready_state() socket.STATE_OPEN and socket.get_available_packet_count(): message_received.emit(get_message())其中get_message()会区分文本帧与二进制帧文本帧get_string_from_utf8()解码二进制帧用bytes_to_var反序列化从而把底层字节差异对上层完全屏蔽。此外还提供了close(code, reason)、clear()重建 socket 以便重连、get_socket()获取原始 peer 以读取get_close_code()/get_close_reason()/get_selected_protocol()等信息。客户端 UI 逻辑client.gdclient.gd 通过场景中的信号连接见 client.tscn把上述封装接到 UI_on_web_socket_client_connected_to_server连接成功时打印协商得到的协议get_selected_protocol()_on_web_socket_client_connection_closed断开时打印关闭码与原因get_close_code()/get_close_reason()_on_web_socket_client_message_received把收到的消息追加到日志_on_connect_toggledConnect按钮为 toggle 模式按下时校验Host输入框非空后调用connect_to_url再次点击则close()_on_send_pressed发送输入框内容后清空。注意客户端侧对连接结果的校验connect_to_url返回非OK时会打印 Error connecting to host这是 WebSocket 客户端异步握手下快速失败如地址格式错误的兜底提示。服务器封装WebSocketServer 源码逐段解析WebSocketServer.gd 的架构比客户端复杂一档它用TCPServer承接原始 TCP 连接再用WebSocketPeer.accept_stream()逐个升级为 WebSocket 连接。导出配置export var handshake_headers : PackedStringArray() export var supported_protocols : PackedStringArray() export var handshake_timout : 3000 export var use_tls: bool false export var tls_cert: X509Certificate export var tls_key: CryptoKey export var refuse_new_connections: bool false: set(refuse): if refuse: pending_peers.clear()各配置项含义supported_protocols服务器支持的子协议列表握手时与客户端协商本演示在 server.tscn 中设置为PackedStringArray(demo-chat)客户端同样设置demo-chat见 client.tscn从而握手时双方能协商出demo-chat协议handshake_timout握手超时时间毫秒默认 3000ms超时未完成的连接会被清理use_tls/tls_cert/tls_key开启wss://加密服务所需的证书与私钥TLSOptions.server(tls_key, tls_cert)refuse_new_connections置为true时立即清空所有待握手队列用于拒绝新连接不影响已建立连接的会话。连接管理与广播式 send服务器内部维护两类集合pending_peers: Array[PendingPeer]已完成 TCP 连接但尚未完成 WebSocket 握手的准客户端每个PendingPeer记录connect_time用于超时判断、tcp原始流与connection可能被升级为 TLS 流peers: Dictionary已握手成功的 WebSocket 对等端key 是随机生成的整数 peer_idrandi_range(2, 1 30)见_connect_pending。send(peer_id, message)是服务端消息分发的核心支持三种目标func send(peer_id: int, message: String) - int: var type : typeof(message) if peer_id 0: # Send to multiple peers, (zero broadcast, negative exclude one). for id: int in peers: if id -peer_id: continue if type TYPE_STRING: peers[id].send_text(message) else: peers[id].put_packet(message) return OK assert(peers.has(peer_id)) var socket: WebSocketPeer peers[peer_id] if type TYPE_STRING: return socket.send_text(message) return socket.send(var_to_bytes(message))send(0, ...)广播给所有客户端send(-peer_id, ...)排除指定客户端后广播给其余所有人本演示的服务器用这个方式实现把你的消息回显给其他用户send(peer_id, ...)单发给指定客户端。这正好对应 server.gd 中的两个广播调用_on_web_socket_server_client_connected里_server.send(-peer_id, [%d] connected % peer_id)把某客户端上线通知给其他所有人_on_web_socket_server_message_received里_server.send(-peer_id, [%d] Says: %s % [peer_id, message])把消息回显给除发送者之外的所有人。poll完整的状态机驱动poll()每帧由_process调用执行四阶段工作接收新 TCP 连接tcp_server.is_connection_available()时take_connection()包装成PendingPeer加入待握手队列若refuse_new_connections则跳过推进握手对每个PendingPeer调用_connect_pending()成功则分配 peer_id 加入peers并发出client_connected信号失败或超过handshake_timout则移出队列轮询已连接 peer对每个已建立的WebSocketPeer调用poll()状态不再STATE_OPEN时发出client_disconnected信号有包则逐个发出message_received(peer_id, message)信号清理统一移除已断开/失败的 peer。_connect_pending()内部处理了三条路径纯 TCP 直连accept_stream(tcp)、TLS 升级先用StreamPeerTLS.accept_stream(p.tcp, TLSOptions.server(...))完成握手再交给WebSocketPeer、以及仍在握手中的轮询等待——这是理解Godot 服务器如何支持 wss://的关键代码。服务器 UI 逻辑server.gdserver.gd 的信号连接定义在 server.tscn_on_web_socket_server_client_connected打印新客户端 peer_id 与协商协议并广播上线通知_on_web_socket_server_client_disconnected打印断开码与原因并广播下线通知_on_web_socket_server_message_received打印收到的消息然后以send(-peer_id, ...)回显给其他客户端_on_listen_toggledListen按钮 toggle开启时读取SpinBox端口调用_server.listen(port)并打印监听的端口与支持的协议列表关闭时_server.stop()_on_send_pressed服务器自己用send(0, ...)向全体客户端广播 Server says: ...。运行演示仓库是只读的你可以将 networking/websocket_chat 目录以 Godot 4.7 打开或复制到本地后导入直接运行主场景combo.tscn点击左侧服务器面板的Listen看到日志输出Listing on port 8000, supported protocols: [demo-chat]即监听成功依次点击三个客户端面板的ConnectHost默认已是ws://localhost:8000/test/客户端日志会出现Client just connected with protocol: demo-chat在任一客户端的输入框发消息服务器回显[peer_id] Says: 消息且其他两个客户端都能收到发消息的客户端自身不会收到自己的回显-peer_id排除逻辑点击某客户端Connect按钮使其断开服务器与其他客户端会收到[id] disconnected通知。若本机防火墙或端口被占用导致listen失败服务器日志会提示Error listing on port可改用Port控件中的其他端口。延伸把这个演示改造成自己的聊天/消息系统改协议名supported_protocols在 server.tscn 与 client.tscn 同步修改即可双方不一致时握手会失败这是 WebSocket 子协议协商机制连外部服务器客户端封装直接支持wss://设置tls_options即可连接外部加密服务服务器侧把use_tls置为true并配置tls_cert/tls_key就能提供加密服务传二进制数据WebSocketClient.send与WebSocketServer.send对非字符串类型会自动走var_to_bytes/bytes_to_var序列化可直接传Dictionary、Array等 Variant适合扩展成带消息类型字段的结构化协议复用封装websocket/WebSocketClient.gd与websocket/WebSocketServer.gd以class_name全局注册不依赖任何 UI可以直接把这两个文件复制进自己的项目仅靠信号收发消息与界面完全解耦。如果需要查看更多网络相关演示仓库 networking 目录下还提供了 WebRTCwebrtc_minimal、webrtc_signaling、WebSocket 多人在线websocket_multiplayer与多人 Pongmultiplayer_pong等场景可作为理解 Godot 网络栈的后续阅读材料。赞分享示例工程【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址https://gitcode.com/GitHub_Trending/go/godot-demo-projects点击查看免费下载相关推荐Java-WebSocket单元测试模拟WebSocket服务器与客户端Java WebSocket单元测试模拟WebSocket服务器与客户端 1. 单元测试痛点与解决方案 在WebSocket套接字应用开发中开发者常面临后端WebSocket网络通信cpp-httplib WebSocket 实战用 C 实现双端 Echo 服务器与客户端cpp httplib WebSocket 实战用 C 实现双端 Echo 服务器与客户端 WebSocket 是建立在 HTTP Upgrade 之上的后端网络wsNode.js 高性能 WebSocket 客户端与服务端实战指南wsNode.js 高性能 WebSocket 客户端与服务端实战指南 ws 是一个专为 Node.js 设计的 WebSocket 客户端与服务端实现以后端通信上一篇Zoom Phone CRM 集成参考样板验证从 CRM-Sample 提炼架构模式与迁移漂移治理下一篇SkillSpector 架构深潜并发模型、线程安全与 contrib 批量扫描层的设计实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表