feat(network): 集成ENet库并实现局域网联机对战功能

- 添加ENet库作为网络通信基础,替换原有的原生Socket实现
- 扩展游戏模式支持局域网联机对战(PvP网络模式)
- 重构网络状态结构以适配ENet的Host/Peer模型
- 在图形界面中添加网络对战菜单,支持创建房间和加入房间
- 实现网络消息的发送与接收,包括落子、断开连接等消息类型
- 为网络对战添加定时器轮询机制,实时处理网络事件
- 更新构建系统以编译和链接ENet库
This commit is contained in:
2026-03-17 17:57:04 +08:00
parent 0c9cfba81b
commit 88f12bcfea
50 changed files with 10969 additions and 241 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ Ihandle *board_canvas = NULL;
Ihandle *lbl_player = NULL;
Ihandle *lbl_status = NULL;
int gui_loop_running = 0;
int gui_game_mode = 0; // 0: PvP, 1: PvE, 2: Replay
int gui_game_mode = 0; // 0: PvP, 1: PvE, 2: Replay, 3: Network
int replay_total_steps = 0; // 复盘总步数
/**
+133
View File
@@ -5,12 +5,73 @@
#include "gobang.h"
#include "ai.h"
#include "record.h"
#include "network.h"
#include <iup.h>
#include <iupdraw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static Ihandle *timer = NULL; // 网络轮询定时器
/**
* @brief 网络事件轮询回调
*/
static int timer_cb(Ihandle *ih)
{
(void)ih;
if (gui_game_mode != 3 || game_over)
return IUP_DEFAULT;
NetworkMessage msg;
// 非阻塞接收消息
if (receive_network_message(&msg, 0))
{
if (msg.type == MSG_MOVE)
{
int bx = msg.x;
int by = msg.y;
int pid = msg.player_id;
if (have_space(bx, by))
{
player_move(bx, by, pid);
if (check_win(bx, by, pid))
{
game_over = 1;
sprintf(status_message, "对手获胜!");
IupMessage("游戏结束", "对手获胜!");
}
else
{
current_player_gui = network_state.local_player_id;
sprintf(status_message, "轮到你落子");
}
update_ui_labels();
if (board_canvas)
IupUpdate(board_canvas);
}
}
else if (msg.type == MSG_DISCONNECT || msg.type == MSG_SURRENDER)
{
game_over = 1;
sprintf(status_message, "对手已断开连接/认输");
IupMessage("游戏结束", "对手已退出游戏,你赢了!");
update_ui_labels();
}
}
if (!is_network_connected() && !game_over)
{
game_over = 1;
sprintf(status_message, "与服务器断开连接");
IupMessage("错误", "网络连接已断开");
update_ui_labels();
}
return IUP_DEFAULT;
}
/**
* @brief ACTION 回调:负责重绘
*/
@@ -46,6 +107,11 @@ int btn_undo_cb(Ihandle *ih)
{
steps_to_undo = 2; // 悔棋两步(玩家+AI
}
else if (gui_game_mode == 3) // Network
{
IupMessage("提示", "网络模式暂不支持悔棋");
return IUP_DEFAULT;
}
if (step_count >= steps_to_undo)
{
@@ -66,6 +132,7 @@ int btn_undo_cb(Ihandle *ih)
sprintf(status_message, "无法悔棋");
update_ui_labels();
}
return IUP_DEFAULT;
}
@@ -120,6 +187,18 @@ int btn_back_cb(Ihandle *ih)
(void)ih;
printf("DEBUG: Back to Menu clicked\n");
// 如果是网络模式,断开连接
if (gui_game_mode == 3)
{
disconnect_network();
if (timer)
{
IupSetAttribute(timer, "RUN", "NO");
IupDestroy(timer);
timer = NULL;
}
}
// 1. 先显示主菜单
show_main_menu();
printf("DEBUG: Main menu shown\n");
@@ -150,6 +229,11 @@ int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status)
if (game_over)
return IUP_DEFAULT;
if (gui_game_mode == 3 && current_player_gui != network_state.local_player_id)
{
return IUP_DEFAULT; // 网络模式下,非自己回合不可落子
}
int board_x, board_y;
if (screen_to_board(x, y, &board_x, &board_y))
{
@@ -162,6 +246,8 @@ int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status)
if (check_win(board_x, board_y, current_player_gui))
{
game_over = 1;
if (gui_game_mode == 3)
send_move(board_x, board_y, current_player_gui); // 发送最后一步
if (current_player_gui == PLAYER)
{
sprintf(status_message, "黑子获胜!");
@@ -183,6 +269,12 @@ int button_cb(Ihandle *ih, int button, int pressed, int x, int y, char *status)
else
sprintf(status_message, "轮到白子");
}
else if (gui_game_mode == 3) // Network
{
send_move(board_x, board_y, current_player_gui);
current_player_gui = network_state.remote_player_id;
sprintf(status_message, "等待对手落子...");
}
else // PvE
{
current_player_gui = AI;
@@ -396,3 +488,44 @@ void start_pve_game_gui()
}
printf("DEBUG: start_pve_game_gui end\n");
}
void start_network_game_gui()
{
printf("DEBUG: start_network_game_gui start\n");
gui_game_mode = 3;
empty_board();
// 主机执黑先行
current_player_gui = PLAYER1;
game_over = 0;
create_game_window();
printf("DEBUG: create_game_window returned\n");
if (dlg)
{
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
printf("DEBUG: IupShowXY called\n");
}
if (network_state.is_server)
sprintf(status_message, "局域网联机 - 你是主机(黑子),轮到你落子");
else
sprintf(status_message, "局域网联机 - 你是客机(白子),等待对手落子...");
update_ui_labels();
// 强制初始重绘
if (board_canvas)
{
IupUpdate(board_canvas);
}
// 启动网络轮询定时器
timer = IupTimer();
IupSetCallback(timer, "ACTION_CB", (Icallback)timer_cb);
IupSetAttribute(timer, "TIME", "50"); // 50ms 轮询一次
IupSetAttribute(timer, "RUN", "YES");
printf("DEBUG: start_network_game_gui end\n");
}
+111 -1
View File
@@ -1,12 +1,15 @@
#include <iup.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gui_menu.h"
#include "gui.h"
#include "gui_internal.h"
#include "globals.h"
#include "config.h"
#include "network.h"
static Ihandle *menu_dlg = NULL;
Ihandle *menu_dlg = NULL;
static int btn_pvp_cb(Ihandle *ih)
{
@@ -28,6 +31,107 @@ static int btn_pve_cb(Ihandle *ih)
return IUP_DEFAULT;
}
// --- 网络对战相关回调 ---
static int btn_network_host_cb(Ihandle *ih)
{
Ihandle *dlg = IupGetDialog(ih);
Ihandle *txt_port = IupGetDialogChild(dlg, "NET_PORT");
int port = IupGetInt(txt_port, "VALUE");
if (port <= 0 || port > 65535) port = DEFAULT_PORT;
if (create_server(port))
{
IupMessage("成功", "房间创建成功,等待玩家加入...");
IupHide(dlg);
start_network_game_gui();
IupHide(menu_dlg);
}
else
{
IupMessage("错误", "创建房间失败,可能是端口被占用");
}
return IUP_DEFAULT;
}
static int btn_network_join_cb(Ihandle *ih)
{
Ihandle *dlg = IupGetDialog(ih);
Ihandle *txt_ip = IupGetDialogChild(dlg, "NET_IP");
Ihandle *txt_port = IupGetDialogChild(dlg, "NET_PORT");
char *ip = IupGetAttribute(txt_ip, "VALUE");
int port = IupGetInt(txt_port, "VALUE");
if (port <= 0 || port > 65535) port = DEFAULT_PORT;
if (connect_to_server(ip, port))
{
IupMessage("成功", "成功加入房间!");
IupHide(dlg);
start_network_game_gui();
IupHide(menu_dlg);
}
else
{
IupMessage("错误", "加入房间失败,请检查IP和端口");
}
return IUP_DEFAULT;
}
static int btn_network_cancel_cb(Ihandle *ih)
{
Ihandle *dlg = IupGetDialog(ih);
IupHide(dlg);
return IUP_DEFAULT;
}
static int btn_network_cb(Ihandle *ih)
{
(void)ih;
printf("DEBUG: Opening Network Menu\n");
Ihandle *txt_ip = IupText(NULL);
IupSetAttribute(txt_ip, "NAME", "NET_IP");
IupSetAttribute(txt_ip, "VALUE", "127.0.0.1");
IupSetAttribute(txt_ip, "SIZE", "100x");
Ihandle *txt_port = IupText(NULL);
IupSetAttribute(txt_port, "NAME", "NET_PORT");
char port_str[16];
sprintf(port_str, "%d", DEFAULT_PORT);
IupSetAttribute(txt_port, "VALUE", port_str);
IupSetAttribute(txt_port, "SIZE", "50x");
Ihandle *btn_host = IupButton("创建房间", NULL);
IupSetCallback(btn_host, "ACTION", (Icallback)btn_network_host_cb);
Ihandle *btn_join = IupButton("加入房间", NULL);
IupSetCallback(btn_join, "ACTION", (Icallback)btn_network_join_cb);
Ihandle *btn_cancel = IupButton("取消", NULL);
IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_network_cancel_cb);
Ihandle *vbox = IupVbox(
IupHbox(IupLabel("目标 IP: "), txt_ip, NULL),
IupHbox(IupLabel("端口: "), txt_port, NULL),
IupLabel(""),
IupHbox(btn_host, btn_join, btn_cancel, NULL),
NULL
);
IupSetAttribute(vbox, "MARGIN", "20x20");
IupSetAttribute(vbox, "GAP", "10");
IupSetAttribute(vbox, "ALIGNMENT", "ACENTER");
Ihandle *dlg = IupDialog(vbox);
IupSetAttribute(dlg, "TITLE", "局域网联机");
IupSetAttribute(dlg, "RESIZE", "NO");
IupPopup(dlg, IUP_CENTER, IUP_CENTER);
IupDestroy(dlg);
return IUP_DEFAULT;
}
// --- 网络对战结束 ---
static int btn_replay_cb(Ihandle *ih)
{
(void)ih;
@@ -227,6 +331,11 @@ void create_main_menu()
IupSetAttribute(btn_pve, "SIZE", "120x30");
IupSetAttribute(btn_pve, "FONT", "SimHei, 12");
Ihandle *btn_net = IupButton("局域网联机", NULL);
IupSetCallback(btn_net, "ACTION", (Icallback)btn_network_cb);
IupSetAttribute(btn_net, "SIZE", "120x30");
IupSetAttribute(btn_net, "FONT", "SimHei, 12");
Ihandle *btn_replay = IupButton("复盘模式", NULL);
IupSetCallback(btn_replay, "ACTION", (Icallback)btn_replay_cb);
IupSetAttribute(btn_replay, "SIZE", "120x30");
@@ -246,6 +355,7 @@ void create_main_menu()
lbl_title,
btn_pvp,
btn_pve,
btn_net,
btn_replay,
btn_settings,
btn_exit,
+1 -1
View File
@@ -4,7 +4,7 @@
* @note 本文件包含了游戏的主循环、模式选择和游戏初始化等功能
* @brief 将以下指令复制到powershell
*
* !图形化版本编译(需要IUP库):
* !编译(需要IUP库):
* mingw32-make gui
.\bin\gobang_gui.exe
*
+158 -212
View File
@@ -12,41 +12,20 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket close
typedef int SOCKET;
#endif
#include <enet/enet.h>
/**
* @brief 初始化网络模块
*/
bool init_network()
{
#ifdef _WIN32
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
if (enet_initialize() != 0)
{
printf("WSAStartup failed: %d\n", result);
printf("An error occurred while initializing ENet.\n");
return false;
}
#endif
memset(&network_state, 0, sizeof(NetworkGameState));
network_state.socket = INVALID_SOCKET;
network_state.port = DEFAULT_PORT;
return true;
@@ -57,16 +36,15 @@ bool init_network()
*/
void cleanup_network()
{
if (network_state.socket != INVALID_SOCKET)
disconnect_network();
if (network_state.host != NULL)
{
closesocket(network_state.socket);
network_state.socket = INVALID_SOCKET;
enet_host_destroy((ENetHost *)network_state.host);
network_state.host = NULL;
}
#ifdef _WIN32
WSACleanup();
#endif
enet_deinitialize();
network_state.is_connected = false;
}
@@ -75,43 +53,23 @@ void cleanup_network()
*/
bool create_server(int port)
{
struct sockaddr_in server_addr, client_addr;
int addr_len = sizeof(client_addr);
ENetAddress address;
// 创建套接字
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == INVALID_SOCKET)
// 绑定所有接口
address.host = ENET_HOST_ANY;
address.port = port;
// 创建服务器主机
network_state.host = (void *)enet_host_create(&address,
1, // 仅允许1个客户端连接
2, // 允许2个通道 (0 和 1)
0, // 假设传入带宽无限制
0 // 假设传出带宽无限制
);
if (network_state.host == NULL)
{
printf("创建套接字失败\n");
return false;
}
// 设置地址重用
int opt = 1;
#ifdef _WIN32
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
#else
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#endif
// 绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
if (bind(listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
{
printf("绑定端口失败\n");
closesocket(listen_socket);
return false;
}
// 开始监听
if (listen(listen_socket, 1) == SOCKET_ERROR)
{
printf("监听失败\n");
closesocket(listen_socket);
printf("创建服务器失败\n");
return false;
}
@@ -127,29 +85,31 @@ bool create_server(int port)
printf("服务器已启动,监听端口: %d\n", port);
}
// 等待客户端连接
SOCKET client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, &addr_len);
if (client_socket == INVALID_SOCKET)
// 阻塞等待客户端连接
ENetEvent event;
printf("等待连接...\n");
// 等待长一点的时间,比如60秒,或者在真实应用中应该放在循环里非阻塞检查
if (enet_host_service((ENetHost *)network_state.host, &event, 60000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT)
{
printf("接受连接失败\n");
closesocket(listen_socket);
return false;
network_state.peer = (void *)event.peer;
network_state.is_server = true;
network_state.is_connected = true;
network_state.local_player_id = PLAYER1;
network_state.remote_player_id = PLAYER2;
network_state.port = port;
enet_address_get_host_ip(&event.peer->address, network_state.remote_ip, sizeof(network_state.remote_ip));
printf("客户端已连接: %s\n", network_state.remote_ip);
return true;
}
// 关闭监听套接字
closesocket(listen_socket);
// 保存连接信息
network_state.socket = client_socket;
network_state.is_server = true;
network_state.is_connected = true;
network_state.local_player_id = PLAYER1;
network_state.remote_player_id = PLAYER2;
network_state.port = port;
strcpy(network_state.remote_ip, inet_ntoa(client_addr.sin_addr));
printf("客户端已连接: %s\n", network_state.remote_ip);
return true;
// 超时或失败
printf("等待连接超时或失败\n");
enet_host_destroy((ENetHost *)network_state.host);
network_state.host = NULL;
return false;
}
/**
@@ -157,55 +117,60 @@ bool create_server(int port)
*/
bool connect_to_server(const char *ip, int port)
{
struct sockaddr_in server_addr;
// 创建客户端主机
network_state.host = (void *)enet_host_create(NULL, // 创建客户端
1, // 仅允许1个传出连接
2, // 允许2个通道 (0 和 1)
0, // 假设传入带宽无限制
0 // 假设传出带宽无限制
);
// 创建套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == INVALID_SOCKET)
if (network_state.host == NULL)
{
printf("创建套接字失败\n");
printf("创建客户端主机失败\n");
return false;
}
// 设置服务器地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
ENetAddress address;
ENetEvent event;
ENetPeer *peer;
#ifdef _WIN32
server_addr.sin_addr.s_addr = inet_addr(ip);
if (server_addr.sin_addr.s_addr == INADDR_NONE)
{
#else
if (inet_pton(AF_INET, ip, &server_addr.sin_addr) <= 0)
{
#endif
printf("无效的IP地址: %s\n", ip);
closesocket(client_socket);
return false;
}
enet_address_set_host(&address, ip);
address.port = port;
printf("正在连接到服务器 %s:%d...\n", ip, port);
// 连接到服务器
if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
peer = enet_host_connect((ENetHost *)network_state.host, &address, 2, 0);
if (peer == NULL)
{
printf("连接服务器失败\n");
closesocket(client_socket);
printf("没有可用的对等端来启动连接\n");
enet_host_destroy((ENetHost *)network_state.host);
network_state.host = NULL;
return false;
}
// 保存连接信息
network_state.socket = client_socket;
network_state.is_server = false;
network_state.is_connected = true;
network_state.local_player_id = PLAYER2;
network_state.remote_player_id = PLAYER1;
network_state.port = port;
strcpy(network_state.remote_ip, ip);
// 等待连接成功
if (enet_host_service((ENetHost *)network_state.host, &event, 5000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT)
{
network_state.peer = (void *)peer;
network_state.is_server = false;
network_state.is_connected = true;
network_state.local_player_id = PLAYER2;
network_state.remote_player_id = PLAYER1;
network_state.port = port;
strncpy(network_state.remote_ip, ip, sizeof(network_state.remote_ip) - 1);
printf("成功连接到服务器\n");
return true;
printf("成功连接到服务器\n");
return true;
}
// 连接失败
printf("连接服务器失败\n");
enet_peer_reset(peer);
enet_host_destroy((ENetHost *)network_state.host);
network_state.host = NULL;
return false;
}
/**
@@ -213,13 +178,21 @@ bool connect_to_server(const char *ip, int port)
*/
bool send_network_message(const NetworkMessage *msg)
{
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
if (!network_state.is_connected || network_state.peer == NULL)
{
return false;
}
int bytes_sent = send(network_state.socket, (const char *)msg, sizeof(NetworkMessage), 0);
return bytes_sent == sizeof(NetworkMessage);
ENetPacket *packet = enet_packet_create(msg, sizeof(NetworkMessage), ENET_PACKET_FLAG_RELIABLE);
if (enet_peer_send((ENetPeer *)network_state.peer, 0, packet) < 0)
{
enet_packet_destroy(packet); // 发送失败需手动销毁
return false;
}
// 强制发送
enet_host_flush((ENetHost *)network_state.host);
return true;
}
/**
@@ -227,51 +200,44 @@ bool send_network_message(const NetworkMessage *msg)
*/
bool receive_network_message(NetworkMessage *msg, int timeout_ms)
{
if (!network_state.is_connected || network_state.socket == INVALID_SOCKET)
if (!network_state.is_connected || network_state.host == NULL)
{
return false;
}
// 设置超时
if (timeout_ms > 0)
{
#ifdef _WIN32
DWORD timeout = timeout_ms;
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
#else
struct timeval timeout;
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
setsockopt(network_state.socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#endif
}
ENetEvent event;
int serviceResult = enet_host_service((ENetHost *)network_state.host, &event, timeout_ms);
int bytes_received = recv(network_state.socket, (char *)msg, sizeof(NetworkMessage), 0);
if (serviceResult > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_RECEIVE:
if (event.packet->dataLength == sizeof(NetworkMessage))
{
memcpy(msg, event.packet->data, sizeof(NetworkMessage));
enet_packet_destroy(event.packet);
return true;
}
enet_packet_destroy(event.packet);
break;
if (bytes_received == sizeof(NetworkMessage))
{
return true;
}
else if (bytes_received == 0)
{
// 连接已关闭
network_state.is_connected = false;
printf("对方已断开连接\n");
}
else if (bytes_received == SOCKET_ERROR)
{
#ifdef _WIN32
int error = WSAGetLastError();
if (error != WSAETIMEDOUT)
{
#else
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
#endif
case ENET_EVENT_TYPE_DISCONNECT:
network_state.is_connected = false;
printf("网络接收错误\n");
printf("对方已断开连接\n");
network_state.peer = NULL;
break;
case ENET_EVENT_TYPE_NONE:
case ENET_EVENT_TYPE_CONNECT:
break;
}
}
else if (serviceResult < 0)
{
network_state.is_connected = false;
printf("网络接收错误\n");
}
return false;
}
@@ -281,17 +247,34 @@ bool receive_network_message(NetworkMessage *msg, int timeout_ms)
*/
void disconnect_network()
{
if (network_state.is_connected)
if (network_state.is_connected && network_state.peer != NULL)
{
NetworkMessage msg = {0};
msg.type = MSG_DISCONNECT;
msg.player_id = network_state.local_player_id;
msg.timestamp = time(NULL);
ENetEvent event;
send_network_message(&msg);
enet_peer_disconnect((ENetPeer *)network_state.peer, 0);
// 等待断开确认
while (enet_host_service((ENetHost *)network_state.host, &event, 3000) > 0)
{
switch (event.type)
{
case ENET_EVENT_TYPE_RECEIVE:
enet_packet_destroy(event.packet);
break;
case ENET_EVENT_TYPE_DISCONNECT:
puts("断开连接成功");
goto DONE;
default:
break;
}
}
// 超时强制重置
enet_peer_reset((ENetPeer *)network_state.peer);
DONE:
network_state.is_connected = false;
network_state.peer = NULL;
}
cleanup_network();
}
/**
@@ -299,7 +282,7 @@ void disconnect_network()
*/
bool is_network_connected()
{
return network_state.is_connected && network_state.socket != INVALID_SOCKET;
return network_state.is_connected && network_state.peer != NULL;
}
/**
@@ -307,50 +290,13 @@ bool is_network_connected()
*/
bool get_local_ip(char *ip_buffer, int buffer_size)
{
#ifdef _WIN32
// Windows实现
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) == 0)
{
struct hostent *host_entry = gethostbyname(hostname);
if (host_entry != NULL)
{
struct in_addr addr;
addr.s_addr = *((unsigned long *)host_entry->h_addr_list[0]);
strncpy(ip_buffer, inet_ntoa(addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
return true;
}
}
#else
// Linux实现
struct sockaddr_in addr;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != -1)
{
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("8.8.8.8");
addr.sin_port = htons(80);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
{
socklen_t addr_len = sizeof(addr);
if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) == 0)
{
strncpy(ip_buffer, inet_ntoa(addr.sin_addr), buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
close(sock);
return true;
}
}
close(sock);
}
#endif
// 默认返回本地回环地址
strncpy(ip_buffer, "127.0.0.1", buffer_size - 1);
// ENet 没有直接获取本机局域网 IP 的简单跨平台函数。
// 这里我们可以回退到原生 socket 方法,或者简单返回本地回环。
// 为了不引入额外的系统头文件,暂时返回通用提示。
// 在真实应用中,可以保留之前的 gethostname/gethostbyname 逻辑。
strncpy(ip_buffer, "查看本机网络适配器", buffer_size - 1);
ip_buffer[buffer_size - 1] = '\0';
return false;
return true; // 总是返回 true 以允许服务器继续启动
}
/**