首页 / 资讯中心 / 文章详情

Godot SocketServer 基类完全指南:构建 TCP 与 Unix Domain Socket 服务端

Godot SocketServer 基类完全指南:构建 TCP 与 Unix Domain Socket 服务端 ★ FEATURED ARTICLE
文档教程游戏开发【免费下载链接】godot-docsGodot Engine official documentation项目地址https://gitcode.com/GitHub_Trending/go/godot-docs点击查看免费下载SocketServer 是 Godot 引擎中所有基于 Socket 的服务端的抽象基类它定义了服务端生命周期管理的统一 API监听、查询、接收连接、停止。本文以官方类文档为核心结合 TCPServer、UDSServer 两个具体子类以及 StreamPeer 流家族系统讲解如何用 Godot 编写可用的 TCP / Unix Domain Socket 服务端与客户端并给出可直接运行的 GDScript 实战示例。读完本文你将掌握 SocketServer 的四个核心方法、listen() 的地址绑定语义、连接接收的正确轮询姿势以及数据收发与版本迁移的完整要点。SocketServer 在 Godot 网络体系中的位置SocketServer 是一个抽象类类文档开头的继承声明清晰地给出了它的位置见 class_socketserver.rstSocketServer RefCounted Object 继承者TCPServer、UDSServer它继承自RefCounted因此由引用计数管理生命周期无需手动释放它被TCPServerTCP 服务器和UDSServerUnix Domain Socket 服务器继承这两个子类才提供了真正可用的listen()方法类描述只有一句话A socket server.一个基于 Socket 的服务器其职责被概括为基于 Socket 的服务器的抽象类An abstract class for servers based on sockets。在 Godot 的流式网络体系中SocketServer 与 StreamPeer 家族构成服务端—连接两条主线角色抽象基类具体实现服务端SocketServerTCPServer、UDSServer连接流StreamPeerSocketStreamPeerTCP、StreamPeerUDS服务端通过take_socket_connection()接受一个连接返回的就是StreamPeerSocket家族的实例随后便可借助StreamPeer的读写 API 进行数据交换。这一设计在 class_streampeersocket.rst 中体现为抽象基类定义基于 Socket 的流的公共行为。需要特别说明的是SocketServer 是 Godot 4.6 中重构出来的抽象基类。根据 upgrading_to_godot_4.6.rst 的迁移记录对应 PR GH-107954原本定义在TCPServer上的is_connection_available()、is_listening()、stop()三个方法被上移到了基类SocketServerStreamPeerTCP上的disconnect_from_host()、get_status()、poll()则被上移到基类StreamPeerSocket。因此在 4.6 及以后版本中所有 Socket 服务端共享同一套生命周期接口。SocketServer 核心 API 详解SocketServer 总共只定义了四个方法构成了一个完整的服务端生命周期查询是否有连接 → 取出连接 → 停止监听。官方文档给出的方法总览如下返回类型方法说明boolis_connection_available()(const)是否有可供取出的连接boolis_listening()(const)服务端当前是否在监听voidstop()停止监听StreamPeerSockettake_socket_connection()若有连接则取出一个返回 StreamPeerSocketis_connection_available()轮询连接队列bool is_connection_available() const返回true表示当前有一个连接可供取出。这是典型的先查询、后取出模式在游戏主循环_process()中反复调用它一旦返回true就调用take_socket_connection()接受该连接。它的const标记表明该方法不会修改实例成员变量调用是只读的。is_listening()查询监听状态bool is_listening() const返回true表示服务端当前正在监听连接。可用于确认listen()是否真正生效或在 UI 中展示服务状态。同样为const方法。stop()停止监听void stop()停止监听不再接受新的连接。注意它只关闭监听并不会主动断开已经接受的连接——已建立的 StreamPeer 连接由各自的流对象独立管理。服务端对象仍可再次调用listen()重新开始监听。take_socket_connection()取出待处理连接StreamPeerSocket take_socket_connection()如果有可用连接返回携带该连接的StreamPeerSocket在TCPServer中具体为StreamPeerTCP在UDSServer中为StreamPeerUDS。通常配合is_connection_available()使用且应放在while循环中一次取完所有排队中的连接。两大具体子类TCPServer 与 UDSServerSocketServer 是抽象类实际使用时必须实例化它的子类。两个子类分别对应基于端口的网络监听和基于文件系统路径的本机进程间通信两种场景。TCPServer按端口监听的 TCP 服务器class_tcpserver.rst 的定义为A TCP server. Listens to connections on a port and returns aStreamPeerTCPwhen it gets an incoming connection.TCP 服务器监听一个端口收到入站连接时返回StreamPeerTCP。TCPServer 在基类之外补充了三个方法返回类型方法说明Errorlisten(port: int, bind_address: String *)在指定端口上监听intget_local_port()(const)返回当前监听的本地端口StreamPeerTCPtake_connection()取出一个可用连接listen()的 bind_address 语义是本类最值得注意的细节官方文档给出了四种取值情况*默认值监听所有可用地址同时覆盖 IPv4 与 IPv60.0.0.0仅监听 IPv4 的所有地址::仅监听 IPv6 的所有地址具体合法地址如192.168.1.101、::1只监听该地址对应的网卡接口若不存在该地址则监听失败。返回值是Error枚举来自GlobalScopeOK表示监听成功。注意这里与基类方法的差异TCPServer.take_connection()的返回类型被收窄为StreamPeerTCP是对基类take_socket_connection()的具象化。Android 平台提示文档明确要求导出到 Android 时必须在导出预设中启用INTERNET权限否则任何网络通信都会被系统阻止。UDSServer基于文件路径的 Unix Domain Socket 服务器class_udsserver.rst 的定义为A Unix Domain Socket (UDS) server. Listens to connections on a socket path and returns aStreamPeerUDS...。Unix Domain Socket 通过文件系统命名空间在同一台机器上提供进程间通信IPC不经过网络协议栈天然比 TCP 回环更快、更安全只对本机可见。UDSServer 提供两个方法返回类型方法说明Errorlisten(path: String)在指定路径的 socket 文件上监听StreamPeerUDStake_connection()取出一个可用连接两个重要限制平台限制Unix Domain Socket 仅在类 Unix 系统Linux、macOS 等上可用Windows 不支持socket 文件必须不存在listen(path)会在指定路径创建 socket 文件如果该文件已存在需要先将其删除再调用本方法例如上次异常退出残留的文件。连接侧StreamPeerSocket 与连接状态机take_socket_connection()返回的StreamPeerSocket是另一个抽象基类它定义了连接侧共有的行为见 class_streampeersocket.rst返回类型方法说明voiddisconnect_from_host()断开与主机的连接Statusget_status()(const)返回当前连接状态Errorpoll()轮询 socket、更新其状态其中Status枚举定义了四个状态常量常量值含义STATUS_NONE0初始状态也是断开连接后的状态STATUS_CONNECTING1正在连接主机STATUS_CONNECTED2已连接主机STATUS_ERROR3错误状态StreamPeerTCPTCP 连接端class_streampeertcp.rst 描述它为处理 TCP 连接的流对等体既能主动连接 TCP 服务器也会被 TCP 服务器作为返回结果。它的完整 API返回类型方法说明Errorconnect_to_host(host: String, port: int)连接指定host:port主机名会被解析Errorbind(port: int, host: String *)绑定本地源地址一般不需要Stringget_connected_host()(const)返回对端 IPintget_connected_port()(const)返回对端端口intget_local_port()(const)返回本地绑定端口voidset_no_delay(enabled: bool)关闭/开启 Nagle 算法其中两个方法值得展开bind()文档说明它通常不需要仅在需要强制后续connect_to_host()使用指定host和port作为源地址时才用典型场景是 NAT 穿透NAT punchthrough或强制指定源网卡set_no_delay()为true时数据包立即发送为false默认时使用Nagle 算法延迟并合并小包。文档特别提醒对于发送大包或大量数据的应用建议保持禁用即不要开启 no_delay因为开启会降低可用带宽总量。StreamPeerUDSUDS 连接端class_streampeeruds.rst 是 UDS 的连接端API 更加精简返回类型方法说明Errorconnect_to_host(path: String)连接指定的 Unix Domain Socket 路径Errorbind(path: String)绑定本地源路径一般不需要Stringget_connected_path()(const)返回对端 socket 路径同样受仅类 Unix 系统的平台限制。数据收发StreamPeer 读写 API服务端取出连接后实际的数据收发靠StreamPeer提供的能力StreamPeerSocket继承自StreamPeer见 class_streampeer.rst。StreamPeer描述为主要用于基于流式协议如 TCP的抽象基类提供以原始数据或字符串形式收发数据的 API。字节序与基础类型读写big_endian属性默认false决定编码/解码使用大端还是小端格式通过set_big_endian()/is_big_endian_enabled()访问。在此基础上StreamPeer 提供成对的读写方法类型读取写入有符号 8/16/32/64 位整数get_8()/get_16()/get_32()/get_64()put_8()/put_16()/put_32()/put_64()无符号 8/16/32/64 位整数get_u8()/get_u16()/get_u32()/get_u64()put_u8()/put_u16()/put_u32()/put_u64()浮点数get_float()/get_double()/get_half()put_float()/put_double()/put_half()块数据与字符串put_data(data: PackedByteArray)/get_data(bytes: int)发送/接收一整块数据。get_data返回包含两个元素的Array一个Error常量和一个PackedByteArray若可用字节不足get_data会阻塞直到收满指定数量。put_partial_data()/get_partial_data()非阻塞版本。一次发不完只发一部分put_partial_data返回错误码与实际发送字节数两个值收不足时get_partial_data返回实际收到的字节数。网络编程中建议优先使用partial版本避免卡死。字符串put_string()/get_string()收发 ASCII 字符串put_utf8_string()/get_utf8_string()收发 UTF-8 字符串。两者都会在字符串前写入一个 32 位无符号整数表示长度get_string的bytes参数为-1时自动读取该长度。若不想带长度前缀可直接用put_data(Hello world.to_ascii_buffer())或put_data(Hello world.to_utf8_buffer())。Variant 序列化put_var(value, full_objects false)/get_var(allow_objects false)可以整体编解码任意 Variant内部复用var_to_bytes()/bytes_to_var()机制。文档给出安全警告反序列化对象可能携带会被执行的代码不要对不可信来源的数据启用对象解码否则有远程代码执行风险。实战示例完整的 TCP 回声服务器结合以上 API可以写出一个完整可运行的 TCP 回声服务器。服务端侧的模式与官方 WebSocket 教程websocket.rst 中的 Minimal server example中TCPServer的用法完全一致——该教程正是用_tcp_server.listen(PORT)、while _tcp_server.is_connection_available(): _tcp_server.take_connection()来接受连接的。下面是直接使用StreamPeerTCP的版本extends Node const PORT 9080 var _server : TCPServer.new() var _peers: Array[StreamPeerTCP] [] func _ready() - void: var err : _server.listen(PORT) # 默认 bind_address 为 *监听全部 IPv4/IPv6 地址 if err OK: print(Server started on port %d. % PORT) else: push_error(Unable to start server. Error code: %d % err) set_process(false) func _process(_delta: float) - void: # 轮询并一次性取出所有排队中的连接 while _server.is_connection_available(): var peer: StreamPeerTCP _server.take_connection() _peers.append(peer) print( Peer accepted, local port: %d % peer.get_local_port()) # 处理每个连接先 poll 更新状态再按需读写 for peer in _peers.duplicate(): var err : peer.poll() if err ! OK or peer.get_status() ! StreamPeerSocket.STATUS_CONNECTED: _peers.erase(peer) continue while peer.get_available_bytes() 0: var result : peer.get_partial_data(4096) if result[0] OK: # 原样回显收到的数据 peer.put_data(result[1]) print(Echoed %d bytes. % result[1].size()) func _exit_tree() - void: for peer in _peers: peer.disconnect_from_host() _server.stop() # 停止监听配套的 TCP 客户端GDScriptextends Node func _ready() - void: var client : StreamPeerTCP.new() var err : client.connect_to_host(127.0.0.1, 9080) # 主机名也会被解析 if err ! OK: push_error(Connect failed: %d % err) return # 等待连接建立需要在 _process 中反复调用 poll 推进状态机 var retry : 0 while client.get_status() StreamPeerSocket.STATUS_CONNECTING and retry 100: client.poll() await get_tree().process_frame retry 1 if client.get_status() StreamPeerSocket.STATUS_CONNECTED: client.put_utf8_string(Hello, Godot server!) client.poll() if client.get_available_bytes() 0: print(Server replied: %s % client.get_utf8_string()) client.disconnect_from_host()UDS 服务器用法几乎相同仅把监听方式从端口换成路径并注意清理残留的 socket 文件var _uds_server : UDSServer.new() var SOCKET_PATH : /tmp/godot_demo.sock func _setup() - void: # socket 文件若已存在如上次异常退出残留需要先删除 var err : _uds_server.listen(SOCKET_PATH) if err OK: print(UDS server listening on %s % SOCKET_PATH)对应的 UDS 客户端用StreamPeerUDS.connect_to_host(/tmp/godot_demo.sock)连接并用get_connected_path()查看对端路径。版本迁移与兼容性提示对从旧版本升级的开发者仓库内的迁移文档提供了两条与本主题直接相关的变更记录Godot 4.6见 upgrading_to_godot_4.6.rstTCPServer的is_connection_available()、is_listening()、stop()上移至基类SocketServerStreamPeerTCP的disconnect_from_host()、get_status()、poll()上移至基类StreamPeerSocket。由于是纯方法上移GDScript 源码兼容现有代码无需修改。Godot 4.0见 upgrading_to_godot_4.rstStreamPeerTCP必须显式调用poll()来更新状态不能再依赖get_status()自动轮询原先的is_connected_to_host()方法被移除改用get_status()判断连接状态。最佳实践与常见问题总是先is_connection_available()再take_socket_connection()并且放在while循环里一次取完避免连接积压这也是官方 WebSocket 教程采用的模式。每个 StreamPeer 都要定期poll()从 4.0 起状态更新依赖显式 poll不在每帧调用会导致连接状态不推进、数据不流动。优先使用get_partial_data()/put_partial_data()get_data()在数据不足时会阻塞放在_process()中可能卡住主循环partial版本更适合游戏主循环。监听前清理状态UDS 的 socket 文件必须不存在listen()失败时检查返回值Error而非假设成功。平台差异UDS 仅限类 Unix 系统导出 Android 时必须启用INTERNET权限否则包括 TCP 在内的一切网络通信都会被系统屏蔽。带宽取舍默认 Nagle 算法适合小包高频场景但大数据传输时应通过set_no_delay(false)保持默认聚合避免无谓的带宽损失。相关文档与源码位置类参考文档见 class_socketserver.rst、class_tcpserver.rst、class_udsserver.rst、class_streampeersocket.rst、class_streampeertcp.rst、class_streampeeruds.rst 与 class_streampeer.rst服务端实际使用范例可参考 tutorials/networking/websocket.rst 的 Minimal server example版本迁移细节见 tutorials/migrating/upgrading_to_godot_4.6.rst 与 tutorials/migrating/upgrading_to_godot_4.rst。赞分享文档教程游戏开发【免费下载链接】godot-docsGodot Engine official documentation项目地址https://gitcode.com/GitHub_Trending/go/godot-docs点击查看免费下载相关推荐Wand-Enhancer 完整指南从源码构建到免费解锁 Wand 高级功能Wand Enhancer 完整指南从源码构建到免费解锁 Wand 高级功能 Wand Enhancer 是一个开源的本地补丁工具免费解锁 WandWeM桌面应用前端gRPC Python 如何用 Unix Domain Socketunix: 地址绑定服务并让客户端连接gRPC Python 如何用 Unix Domain Socketunix: 地址绑定服务并让客户端连接 如果你的 Python gRPC 服务不需要走后端RPC框架微服务通信gevent 基于 Unix Domain Socket 的 WSGI 服务器实战unixsocket_server.py 深度解析gevent 基于 Unix Domain Socket 的 WSGI 服务器实战unixsocket_server.py 深度解析 本指南以 gevent后端上一篇终极指南Crow框架并发模型解析与多线程优化技巧下一篇ReSwift单元测试指南如何测试状态变更与Action响应创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站