使用UrsinaNetworking实现游戏服务器重启时遇Socket错误求助
问题分析与解决方案
你的错误根源在于:关闭服务器socket后,UrsinaNetworkingServer的内部监听线程仍在运行,尝试使用已关闭的socket进行IO操作;同时旧服务器的事件绑定没有清理,新服务器启动后存在资源冲突,加上端口可能未及时释放,导致重启时触发socket错误。
具体修复步骤:
1. 完善UrsinaNetworkingServer.close()方法
需要停止内部监听线程,并关闭所有已连接的客户端socket,不能只关闭主服务器socket:
import threading import socket class UrsinaNetworkingServer: # 保留原有类代码... def close(self): # 终止监听线程的循环标志(需确认类内是否有该标志,没有则自行添加) self.running = False # 关闭所有已连接的客户端socket for client in self.clients.values(): try: client.socket.shutdown(socket.SHUT_RDWR) client.socket.close() except Exception: pass # 关闭主服务器socket self.server.shutdown(socket.SHUT_RDWR) self.server.close() # 等待监听线程终止,避免残留线程报错 if hasattr(self, 'listener_thread') and self.listener_thread.is_alive(): self.listener_thread.join(timeout=1) print("Server closed completely")
注:如果UrsinaNetworkingServer内部的监听线程控制变量不是running、线程对象不是listener_thread,请对应修改变量名。
2. 在GenerateServer()中重新注册事件
@Server.event是绑定到旧Server实例的,重启新服务器后必须重新注册EndGame事件:
def GenerateServer(): print("Hello from the server !") global Server Server = UrsinaNetworkingServer("localhost", 25565) global Easy Easy = EasyUrsinaNetworkingServer(Server) # 把事件注册移到这里,确保绑定到新的Server实例 @Server.event def EndGame(Client, when): Server.close() GenerateServer()
3. 启用端口复用
在创建服务器socket时设置SO_REUSEADDR选项,避免端口处于TIME_WAIT状态无法立即复用:
找到UrsinaNetworkingServer中创建socket的代码,添加端口复用配置:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 添加这一行启用端口复用 self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server.bind((self.host, self.port)) self.server.listen()
4. 清理全局变量引用
确保重启时新的Server实例完全覆盖旧的全局变量,避免旧实例的残留引用导致资源冲突。
完成以上修改后,服务器关闭时会彻底清理所有连接和线程资源,重启时创建全新的服务器实例并绑定事件,同时端口可立即复用,即可解决你遇到的socket错误。
内容的提问来源于stack exchange,提问作者Dinorigami
相关产品推荐
相关产品推荐

