2. WAN连接状态监测与状态转换

任务说明

实现WAN口连接状态监测状态机,以物理链路、DHCP地址获取、PPPoE拨号结果作为事件输入,驱动状态在断开(Disconnected)、连接中(Connecting)、已连接(Connected)之间转换,并输出当前状态供上层查询。

核心设计

状态机采用事件驱动模型,事件类型包括:LINK_UP(物理链路建立)、LINK_DOWN(物理链路断开)、DHCP_OK(DHCP获取地址成功)、DHCP_FAIL(DHCP超时或失败)、PPPOE_OK(PPPoE拨号成功)、PPPOE_FAIL(PPPoE拨号失败)。状态转换逻辑如下:

  • 断开:收到LINK_UP后进入连接中,并触发DHCP获取流程;收到其他事件保持断开。
  • 连接中:若当前为DHCP阶段,收到DHCP_OK则进入已连接,收到DHCP_FAIL则回退断开;若当前为PPPoE阶段,收到PPPOE_OK则进入已连接,收到PPPOE_FAIL则回退断开。任何阶段收到LINK_DOWN均回退断开。
  • 已连接:收到LINK_DOWN进入断开;收到其他事件保持已连接。

连接中状态内部区分DHCP阶段与PPPoE阶段,由conn_phase字段标识,初始进入连接中时为DHCP阶段,DHCP成功后若配置了PPPoE则切换为PPPoE阶段并触发拨号,否则直接进入已连接。

完整代码

#include <stdio.h>
#include <string.h>

typedef enum {
    WAN_DISCONNECTED,
    WAN_CONNECTING,
    WAN_CONNECTED
} wan_state_t;

typedef enum {
    EV_LINK_UP,
    EV_LINK_DOWN,
    EV_DHCP_OK,
    EV_DHCP_FAIL,
    EV_PPPOE_OK,
    EV_PPPOE_FAIL
} wan_event_t;

typedef enum {
    PHASE_DHCP,
    PHASE_PPPOE
} conn_phase_t;

typedef struct {
    wan_state_t   state;
    conn_phase_t  phase;
    int           pppoe_enabled;   /* 1表示需要PPPoE拨号 */
    char          ip_addr[16];     /* 获取到的IP地址 */
} wan_conn_t;

void wan_init(wan_conn_t *wan, int pppoe_enabled) {
    memset(wan, 0, sizeof(*wan));
    wan->state = WAN_DISCONNECTED;
    wan->phase = PHASE_DHCP;
    wan->pppoe_enabled = pppoe_enabled;
}

const char* state_name(wan_state_t s) {
    switch (s) {
        case WAN_DISCONNECTED: return "DISCONNECTED";
        case WAN_CONNECTING:   return "CONNECTING";
        case WAN_CONNECTED:    return "CONNECTED";
        default:               return "UNKNOWN";
    }
}

/* 触发DHCP流程(实际环境中调用DHCP客户端) */
static void start_dhcp(wan_conn_t *wan) {
    wan->phase = PHASE_DHCP;
    printf("[ACTION] DHCP discover sent\n");
}

/* 触发PPPoE拨号流程 */
static void start_pppoe(wan_conn_t *wan) {
    wan->phase = PHASE_PPPOE;
    printf("[ACTION] PPPoE dial initiated\n");
}

void wan_handle_event(wan_conn_t *wan, wan_event_t ev) {
    switch (wan->state) {
    case WAN_DISCONNECTED:
        if (ev == EV_LINK_UP) {
            wan->state = WAN_CONNECTING;
            start_dhcp(wan);
        }
        break;

    case WAN_CONNECTING:
        if (ev == EV_LINK_DOWN) {
            wan->state = WAN_DISCONNECTED;
        } else if (wan->phase == PHASE_DHCP) {
            if (ev == EV_DHCP_OK) {
                if (wan->pppoe_enabled) {
                    start_pppoe(wan);
                } else {
                    strcpy(wan->ip_addr, "192.168.1.100");
                    wan->state = WAN_CONNECTED;
                }
            } else if (ev == EV_DHCP_FAIL) {
                wan->state = WAN_DISCONNECTED;
            }
        } else if (wan->phase == PHASE_PPPOE) {
            if (ev == EV_PPPOE_OK) {
                strcpy(wan->ip_addr, "10.0.0.2");
                wan->state = WAN_CONNECTED;
            } else if (ev == EV_PPPOE_FAIL) {
                wan->state = WAN_DISCONNECTED;
            }
        }
        break;

    case WAN_CONNECTED:
        if (ev == EV_LINK_DOWN) {
            wan->state = WAN_DISCONNECTED;
        }
        break;
    }
}

/* 简单验证:模拟事件序列 */
int main() {
    wan_conn_t wan;
    wan_init(&wan, 1);  /* 启用PPPoE */

    printf("Init: %s\n", state_name(wan.state));

    wan_handle_event(&wan, EV_LINK_UP);
    printf("After LINK_UP: %s (phase=%s)\n",
           state_name(wan.state),
           wan.phase == PHASE_DHCP ? "DHCP" : "PPPoE");

    wan_handle_event(&wan, EV_DHCP_OK);
    printf("After DHCP_OK: %s (phase=%s)\n",
           state_name(wan.state),
           wan.phase == PHASE_DHCP ? "DHCP" : "PPPoE");

    wan_handle_event(&wan, EV_PPPOE_OK);
    printf("After PPPOE_OK: %s, IP=%s\n",
           state_name(wan.state), wan.ip_addr);

    wan_handle_event(&wan, EV_LINK_DOWN);
    printf("After LINK_DOWN: %s\n", state_name(wan.state));

    return 0;
}

编译与运行

gcc -o wan_state wan_state.c
./wan_state

验证方法

程序输出应依次为:Init: DISCONNECTED → After LINK_UP: CONNECTING (phase=DHCP) → After DHCP_OK: CONNECTING (phase=PPPoE) → After PPPOE_OK: CONNECTED, IP=10.0.0.2 → After LINK_DOWN: DISCONNECTED。若输出与上述一致,说明状态转换逻辑正确。可修改wan_init的pppoe_enabled参数为0,验证纯DHCP场景下DHCP_OK后直接进入CONNECTED且IP为192.168.1.100。

关键说明

代码中start_dhcp与start_pppoe仅打印动作日志,实际工程中应在此处调用真实DHCP客户端与PPPoE拨号模块,并在其回调中分别上报EV_DHCP_OK/FAIL与EV_PPPOE_OK/FAIL事件。IP地址赋值处为演示逻辑,实际应取自DHCP/PPPoE协商结果。