基于Enet的多人2D射击游戏互联网连接失败问题求助
Enet P2P混合架构跨局域网连接失败问题排查
我正在用Enet开发一款多人2D射击游戏,采用类似《我的世界》LAN世界的P2P与P2C混合架构(玩家可作为主机创建游戏)。尝试通过好友提供的IP连接其主机时,客户端始终无法成功连接,参考Assault Cube的网络实现也没找到解决办法,以下是我的连接实现代码:
bool connectToServer(ENetHost *&client, ENetPeer *&server, int32_t &cid, std::string ip, char *playerName) { ENetAddress adress; ENetEvent event; if (ip.empty()) { enet_address_set_host(&adress, "127.0.0.1"); } else { enet_address_set_host(&adress, ip.c_str()); } //enet_address_set_host(&adress, "95.76.249.14"); //enet_address_set_host(&adress, "192.168.1.11"); adress.port = 7778; //client, adress, channels, data to send rightAway server = enet_host_connect(client, &adress, SERVER_CHANNELS, 0); if (server == nullptr) { return false; } //see if we got events by server //client, event, ms to wait(0 means that we don't wait) if (enet_host_service(client, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { std::cout << "connected\n"; } else { enet_peer_reset(server); return false; } if (enet_host_service(client, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_RECEIVE) { Packet p = {}; size_t size; auto data = parsePacket(event, p, size); // Handshake if (p.header != headerReceiveCIDAndData) { enet_peer_reset(server); return false; } cid = p.cid; glm::vec3 color = *(glm::vec3 *)data; auto e = phisics::Entity(); e.pos = getSpawnPosition(); e.lastPos = e.pos; e.color = color; memcpy(e.name, playerName, playerNameSize); players[cid] = e; sendPlayerData(e, true); std::cout << "received cid: " << cid << "\n"; enet_packet_destroy(event.packet); return true; } else { enet_peer_reset(server); return 0; } return true; }
排查与修复建议
1. 先确认网络连通性
- 如果是外网连接:主机必须在路由器上做7778端口的端口转发,并且确保你使用的是主机的公网IP(不是192.168.x.x这类内网IP)。
- 如果是局域网连接:确认双方在同一网段,关闭双方的防火墙/杀毒软件,或者手动把7778端口加入允许列表。
- 用
ping [目标IP]测试基础连通,再用telnet [目标IP] 7778测试端口是否开放——telnet连不上的话,问题出在网络层面,和代码无关。
2. 检查Enet初始化配置
- 客户端的
ENetHost初始化要符合客户端模式:enet_host_create(NULL, 1, SERVER_CHANNELS, 0, 0),最大连接数设为1即可。 - 主机端的
ENetHost必须绑定到ENET_HOST_ANY,不能只绑定127.0.0.1,否则只有本机能连接。
3. 修复连接逻辑的缺陷
你的代码分两次调用enet_host_service,很容易错过事件导致超时失败,应该用循环统一处理事件:
bool connectToServer(ENetHost *&client, ENetPeer *&server, int32_t &cid, std::string ip, char *playerName) { ENetAddress adress; ENetEvent event; if (ip.empty()) { enet_address_set_host(&adress, "127.0.0.1"); } else { enet_address_set_host(&adress, ip.c_str()); } adress.port = 7778; server = enet_host_connect(client, &adress, SERVER_CHANNELS, 0); if (server == nullptr) return false; bool connected = false; bool handshakeCompleted = false; uint32_t totalWaitMs = 0; const uint32_t maxWaitMs = 10000; // 总超时10秒 while (totalWaitMs < maxWaitMs) { int eventResult = enet_host_service(client, &event, 100); if (eventResult <= 0) { totalWaitMs += 100; continue; } switch (event.type) { case ENET_EVENT_TYPE_CONNECT: std::cout << "已连接到主机\n"; connected = true; break; case ENET_EVENT_TYPE_RECEIVE: if (!connected) break; // 未连接先收到包,直接重置 Packet p = {}; size_t size; auto data = parsePacket(event, p, size); if (p.header == headerReceiveCIDAndData) { cid = p.cid; glm::vec3 color = *(glm::vec3 *)data; phisics::Entity e; e.pos = getSpawnPosition(); e.lastPos = e.pos; e.color = color; memcpy(e.name, playerName, playerNameSize); players[cid] = e; sendPlayerData(e, true); std::cout << "收到CID: " << cid << "\n"; enet_packet_destroy(event.packet); handshakeCompleted = true; } else { enet_peer_reset(server); return false; } break; case ENET_EVENT_TYPE_DISCONNECT: enet_peer_reset(server); return false; } if (connected && handshakeCompleted) break; } if (!handshakeCompleted) { enet_peer_reset(server); return false; } return true; }
- 确保主机和客户端的
SERVER_CHANNELS定义一致,通道数不匹配会直接导致连接失败。 - 原代码末尾的
return true;属于冗余分支,可以删除。
4. 增加调试日志
- 主机端添加日志,确认是否收到客户端的连接请求——如果主机没收到,是网络问题;如果收到但没发握手包,是主机逻辑问题。
- 客户端在调用
enet_address_set_host后,打印解析后的IP,确认输入的IP没有解析错误。
内容的提问来源于stack exchange,提问作者Aspis
相关产品推荐
相关产品推荐

