Linux 环境下 PPP 拨号的嵌入式开发实现

news2025/6/6 22:15:06

一、PPP 协议基础与嵌入式应用场景

        PPP (Point-to-Point Protocol) 是一种在串行线路上传输多协议数据包的通信协议,广泛应用于拨号上网、VPN 和嵌入式系统的远程通信场景。在嵌入式开发中,PPP 常用于 GPRS/3G/4G 模块、工业路由器和物联网设备的网络连接。

        PPP 协议提供了以下核心功能:

  • 链路控制协议 (LCP):建立、配置和测试数据链路
  • 网络层协议 (NCP):协商并配置不同的网络层协议
  • 认证协议:支持 PAP、CHAP 等认证方式
二、Linux 下 PPP 拨号的系统架构

        在 Linux 系统中,PPP 拨号主要涉及以下组件:

  1. PPPD 守护进程:用户空间程序,负责 PPP 链路的建立、维护和终止
  2. Chat 脚本:辅助工具,用于与调制解调器进行 AT 命令交互
  3. 内核 PPP 驱动:提供 PPP 协议的底层实现
  4. 网络配置工具:如 ifconfig、route 等,用于配置拨号后的网络参数

        典型的嵌入式 PPP 拨号系统架构如下:

+---------------------+
|   应用程序/服务      |
+---------------------+
|      PPPD守护进程    |
+---------------------+
|      Chat脚本       |
+---------------------+
|    串口驱动/USB驱动  |
+---------------------+
|      调制解调器      |
+---------------------+
|      网络链路       |
+---------------------+
三、PPP 拨号程序实现方案

        下面介绍在 Linux 嵌入式系统中实现 PPP 拨号的两种主要方案:

        方案一:调用系统命令实现 PPP 拨号

        这是最简单的实现方式,通过 system () 或 popen () 函数调用系统 pppd 命令和 chat 脚本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

// PPP拨号函数
int ppp_dial(const char *device, const char *apn, 
             const char *username, const char *password) {
    pid_t pid;
    int status;
    char cmd[256];
    
    // 构建chat脚本内容
    FILE *chat_file = fopen("/tmp/chatscript", "w");
    if (chat_file == NULL) {
        perror("Failed to create chat script");
        return -1;
    }
    
    fprintf(chat_file, "#!/bin/sh\n");
    fprintf(chat_file, "ABORT 'BUSY'\n");
    fprintf(chat_file, "ABORT 'NO CARRIER'\n");
    fprintf(chat_file, "ABORT 'NO DIALTONE'\n");
    fprintf(chat_file, "ABORT 'ERROR'\n");
    fprintf(chat_file, "TIMEOUT 30\n");
    fprintf(chat_file, "SAY 'Starting PPP dial...\\n'\n");
    fprintf(chat_file, "'' ATZ\\n");
    fprintf(chat_file, "OK AT+CGDCONT=1,\"IP\",\"%s\"\\n", apn);
    fprintf(chat_file, "OK ATD*99#\\n");
    fprintf(chat_file, "CONNECT ''\n");
    fclose(chat_file);
    
    // 设置chat脚本可执行权限
    system("chmod +x /tmp/chatscript");
    
    // 构建pppd命令
    snprintf(cmd, sizeof(cmd), 
             "pppd call /tmp/chatscript %s user %s password %s debug nodetach &",
             device, username, password);
    
    // 执行pppd命令
    pid = fork();
    if (pid < 0) {
        perror("Fork failed");
        return -1;
    } else if (pid == 0) {
        // 子进程执行pppd命令
        execl("/bin/sh", "sh", "-c", cmd, NULL);
        exit(EXIT_FAILURE);
    } else {
        // 父进程等待子进程结束
        waitpid(pid, &status, 0);
        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
            printf("PPP dial successful\n");
            return 0;
        } else {
            printf("PPP dial failed, exit status: %d\n", WEXITSTATUS(status));
            return -1;
        }
    }
}

// PPP断开连接函数
int ppp_hangup() {
    return system("pkill -f pppd");
}

// 检查PPP连接状态
int ppp_check_status() {
    FILE *fp;
    char buffer[128];
    int connected = 0;
    
    // 检查pppd进程是否存在
    fp = popen("ps aux | grep pppd | grep -v grep", "r");
    if (fp != NULL) {
        if (fgets(buffer, sizeof(buffer), fp) != NULL) {
            connected = 1;
        }
        pclose(fp);
    }
    
    // 检查ppp0接口是否存在
    if (connected) {
        fp = popen("ifconfig ppp0", "r");
        if (fp != NULL) {
            if (fgets(buffer, sizeof(buffer), fp) == NULL) {
                connected = 0;
            }
            pclose(fp);
        }
    }
    
    return connected;
}

int main() {
    // PPP参数配置
    const char *device = "/dev/ttyUSB0";  // 调制解调器设备
    const char *apn = "internet";         // APN名称
    const char *username = "";            // 用户名
    const char *password = "";            // 密码
    
    printf("Starting PPP dial...\n");
    
    // 执行PPP拨号
    if (ppp_dial(device, apn, username, password) == 0) {
        printf("PPP connection established\n");
        
        // 检查连接状态
        if (ppp_check_status()) {
            printf("PPP connection is active\n");
            
            // 保持连接一段时间
            sleep(300);
            
            // 断开连接
            printf("Hanging up PPP connection...\n");
            ppp_hangup();
            printf("PPP connection terminated\n");
        } else {
            printf("Failed to establish PPP connection\n");
        }
    } else {
        printf("PPP dial failed\n");
    }
    
    return 0;
}
方案二:直接调用 PPP 库函数实现

        更高级的实现方式是直接调用 PPP 相关的库函数,这种方式提供了更精细的控制:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

// PPP帧结构定义
#define PPP_FLAG        0x7E
#define PPP_ESC         0x7D
#define PPP_XOR         0x20
#define PPP_ADDR        0xFF
#define PPP_CTRL        0x03
#define PPP_LCP         0xC021
#define PPP_PAP         0xC023
#define PPP_IPCP        0x8021
#define PPP_IP          0x0021

// 串口初始化函数
int serial_init(const char *device, int baudrate) {
    int fd;
    struct termios options;
    
    // 打开串口设备
    fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0) {
        perror("Failed to open serial device");
        return -1;
    }
    
    // 获取当前串口配置
    if (tcgetattr(fd, &options) != 0) {
        perror("Failed to get serial attributes");
        close(fd);
        return -1;
    }
    
    // 设置波特率
    switch (baudrate) {
        case 9600:   cfsetispeed(&options, B9600);   cfsetospeed(&options, B9600);   break;
        case 115200: cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); break;
        default:     cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); break;
    }
    
    // 设置串口参数:8数据位,1停止位,无校验
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag |= (CLOCAL | CREAD);
    
    // 设置为原始模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;
    
    // 设置超时参数
    options.c_cc[VMIN] = 0;
    options.c_cc[VTIME] = 10;  // 1秒超时
    
    // 应用新的配置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        perror("Failed to set serial attributes");
        close(fd);
        return -1;
    }
    
    // 清空输入输出缓冲区
    tcflush(fd, TCIOFLUSH);
    
    return fd;
}

// 发送PPP帧
int ppp_send_frame(int fd, unsigned short protocol, const unsigned char *data, int len) {
    unsigned char frame[4096];
    int frame_len = 0;
    int i;
    
    // 添加帧头
    frame[frame_len++] = PPP_FLAG;
    
    // 添加地址和控制字段
    frame[frame_len++] = PPP_ADDR;
    frame[frame_len++] = PPP_CTRL;
    
    // 添加协议字段(大端序)
    frame[frame_len++] = (protocol >> 8) & 0xFF;
    frame[frame_len++] = protocol & 0xFF;
    
    // 添加数据并进行转义
    for (i = 0; i < len; i++) {
        if (data[i] == PPP_FLAG || data[i] == PPP_ESC) {
            frame[frame_len++] = PPP_ESC;
            frame[frame_len++] = data[i] ^ PPP_XOR;
        } else {
            frame[frame_len++] = data[i];
        }
    }
    
    // 添加帧尾
    frame[frame_len++] = PPP_FLAG;
    
    // 发送帧
    return write(fd, frame, frame_len);
}

// 接收PPP帧
int ppp_recv_frame(int fd, unsigned short *protocol, unsigned char *data, int max_len, int timeout_sec) {
    unsigned char buffer[4096];
    int buffer_len = 0;
    int i, j;
    int in_frame = 0;
    int escaped = 0;
    fd_set readfds;
    struct timeval timeout;
    int ret;
    
    // 设置超时
    timeout.tv_sec = timeout_sec;
    timeout.tv_usec = 0;
    
    while (1) {
        // 初始化文件描述符集
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        
        // 等待数据或超时
        ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
        if (ret < 0) {
            perror("Select error");
            return -1;
        } else if (ret == 0) {
            // 超时
            return 0;
        }
        
        // 读取数据
        unsigned char temp[256];
        int n = read(fd, temp, sizeof(temp));
        if (n <= 0) {
            continue;
        }
        
        // 处理接收到的数据
        for (i = 0; i < n; i++) {
            unsigned char c = temp[i];
            
            if (c == PPP_FLAG) {
                if (in_frame && buffer_len >= 5) {
                    // 解析协议字段(大端序)
                    *protocol = (buffer[0] << 8) | buffer[1];
                    
                    // 提取数据
                    int data_len = buffer_len - 4;
                    if (data_len > max_len) {
                        data_len = max_len;
                    }
                    memcpy(data, &buffer[2], data_len);
                    
                    return data_len;
                }
                
                // 开始新帧
                in_frame = 1;
                buffer_len = 0;
                escaped = 0;
                continue;
            }
            
            if (!in_frame) {
                continue;
            }
            
            if (c == PPP_ESC) {
                escaped = 1;
                continue;
            }
            
            if (escaped) {
                c ^= PPP_XOR;
                escaped = 0;
            }
            
            if (buffer_len < sizeof(buffer) - 1) {
                buffer[buffer_len++] = c;
            }
        }
    }
    
    return 0;
}

// 发送AT命令并获取响应
int send_at_command(int fd, const char *command, char *response, int max_len, int timeout_sec) {
    fd_set readfds;
    struct timeval timeout;
    int ret;
    int response_len = 0;
    
    // 发送AT命令
    write(fd, command, strlen(command));
    write(fd, "\r\n", 2);
    
    // 设置超时
    timeout.tv_sec = timeout_sec;
    timeout.tv_usec = 0;
    
    // 读取响应
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
        
        ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
        if (ret < 0) {
            perror("Select error");
            return -1;
        } else if (ret == 0) {
            // 超时
            break;
        }
        
        if (FD_ISSET(fd, &readfds)) {
            char buffer[256];
            int n = read(fd, buffer, sizeof(buffer) - 1);
            if (n > 0) {
                buffer[n] = '\0';
                
                // 追加到响应缓冲区
                if (response_len + n < max_len) {
                    memcpy(response + response_len, buffer, n);
                    response_len += n;
                    response[response_len] = '\0';
                }
                
                // 检查是否收到OK或ERROR
                if (strstr(response, "OK") != NULL || strstr(response, "ERROR") != NULL) {
                    break;
                }
            }
        }
    }
    
    return response_len;
}

// 初始化调制解调器
int modem_init(int fd) {
    char response[1024];
    
    // 重置调制解调器
    send_at_command(fd, "ATZ", response, sizeof(response), 5);
    
    // 设置为命令模式
    send_at_command(fd, "ATE0", response, sizeof(response), 5);
    
    // 检查调制解调器是否就绪
    if (send_at_command(fd, "AT", response, sizeof(response), 5) < 0) {
        return -1;
    }
    
    if (strstr(response, "OK") == NULL) {
        return -1;
    }
    
    return 0;
}

// 配置GPRS连接
int configure_gprs(int fd, const char *apn) {
    char command[128];
    char response[1024];
    
    // 设置APN
    snprintf(command, sizeof(command), "AT+CGDCONT=1,\"IP\",\"%s\"", apn);
    if (send_at_command(fd, command, response, sizeof(response), 10) < 0) {
        return -1;
    }
    
    if (strstr(response, "OK") == NULL) {
        return -1;
    }
    
    return 0;
}

// 建立PPP连接
int establish_ppp_connection(int fd) {
    char response[1024];
    
    // 发起PPP连接
    if (send_at_command(fd, "ATD*99#", response, sizeof(response), 30) < 0) {
        return -1;
    }
    
    // 检查是否连接成功
    if (strstr(response, "CONNECT") == NULL) {
        return -1;
    }
    
    return 0;
}

int main() {
    int fd;
    const char *device = "/dev/ttyUSB0";
    const char *apn = "internet";
    
    // 初始化串口
    fd = serial_init(device, 115200);
    if (fd < 0) {
        printf("Failed to initialize serial port\n");
        return -1;
    }
    
    // 初始化调制解调器
    if (modem_init(fd) < 0) {
        printf("Failed to initialize modem\n");
        close(fd);
        return -1;
    }
    
    // 配置GPRS
    if (configure_gprs(fd, apn) < 0) {
        printf("Failed to configure GPRS\n");
        close(fd);
        return -1;
    }
    
    // 建立PPP连接
    if (establish_ppp_connection(fd) < 0) {
        printf("Failed to establish PPP connection\n");
        close(fd);
        return -1;
    }
    
    printf("PPP connection established successfully\n");
    
    // PPP通信循环
    unsigned short protocol;
    unsigned char data[1024];
    int len;
    
    printf("Waiting for PPP frames...\n");
    
    while (1) {
        len = ppp_recv_frame(fd, &protocol, data, sizeof(data), 5);
        if (len > 0) {
            printf("Received PPP frame: protocol=0x%04X, len=%d\n", protocol, len);
            
            // 处理不同类型的PPP帧
            switch (protocol) {
                case PPP_LCP:
                    printf("  LCP frame\n");
                    // 处理LCP帧
                    break;
                    
                case PPP_IPCP:
                    printf("  IPCP frame\n");
                    // 处理IPCP帧
                    break;
                    
                case PPP_IP:
                    printf("  IP frame\n");
                    // 处理IP数据报
                    break;
                    
                default:
                    printf("  Unknown protocol: 0x%04X\n", protocol);
                    break;
            }
        }
    }
    
    // 关闭连接
    close(fd);
    return 0;
}
四、PPP 拨号配置文件与参数说明

在 Linux 系统中,PPP 拨号通常需要配置以下文件:

  1. /etc/ppp/options:PPP 通用选项配置文件
# PPP通用选项
lock            # 锁定串口设备
crtscts         # 使用硬件流控制
asyncmap 0      # 禁用字符映射
defaultroute    # 添加默认路由
usepeerdns      # 使用DNS服务器提供的IP
  1. /etc/ppp/peers/provider:特定连接的配置文件
# 特定连接配置
/dev/ttyUSB0    # 串口设备
115200          # 波特率
connect '/usr/sbin/chat -v -f /etc/ppp/chatscripts/gprs'  # chat脚本路径
noauth          # 不使用认证
persist         # 保持连接
maxfail 0       # 允许无限次连接尝试
holdoff 2       # 连接失败后等待2秒再尝试
lcp-echo-interval 30  # 每30秒发送一次LCP回显请求
lcp-echo-failure 4    # 连续4次LCP回显请求失败后断开连接
  1. /etc/ppp/chatscripts/gprs:Chat 脚本示例
ABORT "BUSY"
ABORT "NO CARRIER"
ABORT "NO DIALTONE"
ABORT "ERROR"
TIMEOUT 30
SAY "Connecting to GPRS network...\n"
'' ATZ
OK AT+CGDCONT=1,"IP","internet"
OK ATD*99#
CONNECT ""
五、错误处理与调试技巧

在 PPP 拨号过程中,可能会遇到各种问题,以下是一些常见问题及解决方法:

  1. 无法连接到调制解调器

    • 检查串口设备路径是否正确
    • 检查设备权限是否允许访问
    • 使用 minicom 等工具测试串口通信
  2. Chat 脚本执行失败

    • 检查 AT 命令是否正确
    • 增加 Chat 脚本中的调试信息
    • 确认调制解调器支持的 AT 命令集
  3. PPP 连接建立失败

    • 检查 APN、用户名和密码是否正确
    • 查看 /var/log/syslog 或 /var/log/messages 中的 PPP 日志
    • 使用 pppd 的 debug 选项获取详细调试信息
  4. IP 地址分配失败

    • 检查网络服务提供商的 IP 分配策略
    • 确认 IPCP 协商参数是否正确
    • 尝试手动配置 IP 地址

调试 PPP 连接时,可以使用以下命令:

# 以调试模式运行pppd
pppd debug /dev/ttyUSB0 115200 nodetach

# 查看PPP连接状态
ifconfig ppp0
route -n

# 查看PPP日志
tail -f /var/log/syslog | grep pppd
六、PPP 拨号程序的优化与扩展

为了提高 PPP 拨号程序的稳定性和可靠性,可以考虑以下优化措施:

  1. 添加断线自动重连机制
  2. 实现网络连接状态检测
  3. 添加 PPP 进程监控和自动重启功能
  4. 支持多种网络连接方式的切换
  5. 实现 PPP 连接参数的动态配置

以下是一个增强版的 PPP 拨号管理程序框架:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

// 配置参数
#define CONFIG_FILE     "/etc/ppp/config.ini"
#define LOG_FILE        "/var/log/ppp_manager.log"
#define PID_FILE        "/var/run/ppp_manager.pid"
#define CHECK_INTERVAL  30      // 连接检查间隔(秒)
#define RECONNECT_DELAY 10      // 重连延迟(秒)
#define MAX_RETRIES     5       // 最大重试次数

// 全局变量
volatile sig_atomic_t running = 1;
char device[64] = "/dev/ttyUSB0";
char apn[64] = "internet";
char username[64] = "";
char password[64] = "";
int debug_mode = 0;

// 日志函数
void log_message(const char *message) {
    FILE *fp;
    time_t t;
    char time_str[26];
    
    time(&t);
    ctime_r(&t, time_str);
    time_str[24] = '\0';  // 去掉换行符
    
    if (debug_mode) {
        printf("[%s] %s\n", time_str, message);
    }
    
    fp = fopen(LOG_FILE, "a");
    if (fp) {
        fprintf(fp, "[%s] %s\n", time_str, message);
        fclose(fp);
    }
}

// 读取配置文件
int read_config() {
    FILE *fp;
    char line[256];
    
    fp = fopen(CONFIG_FILE, "r");
    if (!fp) {
        log_message("Failed to open config file");
        return -1;
    }
    
    while (fgets(line, sizeof(line), fp)) {
        // 去掉换行符
        line[strcspn(line, "\n")] = 0;
        
        // 跳过注释和空行
        if (line[0] == '#' || line[0] == '\0') {
            continue;
        }
        
        // 解析配置项
        char *key = strtok(line, "=");
        char *value = strtok(NULL, "=");
        
        if (key && value) {
            if (strcmp(key, "device") == 0) {
                strncpy(device, value, sizeof(device) - 1);
            } else if (strcmp(key, "apn") == 0) {
                strncpy(apn, value, sizeof(apn) - 1);
            } else if (strcmp(key, "username") == 0) {
                strncpy(username, value, sizeof(username) - 1);
            } else if (strcmp(key, "password") == 0) {
                strncpy(password, value, sizeof(password) - 1);
            } else if (strcmp(key, "debug") == 0) {
                debug_mode = (strcmp(value, "1") == 0 || strcasecmp(value, "true") == 0);
            }
        }
    }
    
    fclose(fp);
    return 0;
}

// 检查网络连接状态
int check_network_status() {
    // 方法1: 检查ppp0接口是否存在
    FILE *fp = popen("ifconfig ppp0 2>/dev/null", "r");
    if (!fp) {
        return 0;
    }
    
    char buffer[128];
    int exists = (fgets(buffer, sizeof(buffer), fp) != NULL);
    pclose(fp);
    
    if (!exists) {
        return 0;
    }
    
    // 方法2: 尝试ping外部服务器
    fp = popen("ping -c 1 -W 2 8.8.8.8 2>/dev/null", "r");
    if (!fp) {
        return 1;  // 接口存在但ping失败,仍认为连接存在
    }
    
    int connected = 0;
    while (fgets(buffer, sizeof(buffer), fp)) {
        if (strstr(buffer, "1 packets transmitted, 1 received") != NULL) {
            connected = 1;
            break;
        }
    }
    
    pclose(fp);
    return connected;
}

// PPP拨号函数
int ppp_dial() {
    pid_t pid;
    int status;
    char cmd[256];
    
    log_message("Starting PPP dial...");
    
    // 构建pppd命令
    snprintf(cmd, sizeof(cmd), 
             "pppd call provider %s user %s password %s debug nodetach",
             device, username, password);
    
    // 执行pppd命令
    pid = fork();
    if (pid < 0) {
        log_message("Fork failed");
        return -1;
    } else if (pid == 0) {
        // 子进程执行pppd命令
        execl("/bin/sh", "sh", "-c", cmd, NULL);
        exit(EXIT_FAILURE);
    } else {
        // 父进程等待子进程结束
        waitpid(pid, &status, 0);
        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
            log_message("PPP dial successful");
            return 0;
        } else {
            log_message("PPP dial failed");
            return -1;
        }
    }
}

// PPP断开连接函数
int ppp_hangup() {
    log_message("Hanging up PPP connection...");
    return system("pkill -f pppd");
}

// 信号处理函数
void signal_handler(int signum) {
    switch (signum) {
        case SIGINT:
        case SIGTERM:
            log_message("Received termination signal, exiting...");
            running = 0;
            break;
        case SIGHUP:
            log_message("Received reload signal, reloading configuration...");
            read_config();
            break;
    }
}

// 守护进程化
void daemonize() {
    pid_t pid, sid;
    
    // 第一步fork
    pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    
    if (pid > 0) {
        exit(EXIT_SUCCESS);  // 父进程退出
    }
    
    // 创建新会话
    sid = setsid();
    if (sid < 0) {
        exit(EXIT_FAILURE);
    }
    
    // 第二步fork
    pid = fork();
    if (pid < 0) {
        exit(EXIT_FAILURE);
    }
    
    if (pid > 0) {
        exit(EXIT_SUCCESS);  // 父进程退出
    }
    
    // 改变工作目录
    if (chdir("/") < 0) {
        exit(EXIT_FAILURE);
    }
    
    // 关闭文件描述符
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    
    // 创建PID文件
    FILE *fp = fopen(PID_FILE, "w");
    if (fp) {
        fprintf(fp, "%d\n", getpid());
        fclose(fp);
    }
}

int main(int argc, char *argv[]) {
    int daemon = 1;
    int retries = 0;
    
    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
            daemon = 0;
            debug_mode = 1;
        } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            printf("Usage: %s [-d|--debug] [-h|--help]\n", argv[0]);
            return 0;
        }
    }
    
    // 读取配置文件
    if (read_config() < 0) {
        return 1;
    }
    
    // 守护进程化
    if (daemon) {
        daemonize();
    }
    
    // 注册信号处理函数
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    signal(SIGHUP, signal_handler);
    
    log_message("PPP Manager started");
    
    // 主循环
    while (running) {
        if (!check_network_status()) {
            log_message("Network connection lost, attempting to reconnect...");
            
            // 断开当前连接
            ppp_hangup();
            sleep(2);
            
            // 尝试重新连接
            int success = 0;
            retries = 0;
            
            while (retries < MAX_RETRIES && running) {
                retries++;
                log_message("Reconnect attempt %d of %d", retries, MAX_RETRIES);
                
                if (ppp_dial() == 0) {
                    // 等待连接稳定
                    sleep(5);
                    
                    if (check_network_status()) {
                        log_message("Network reconnected successfully");
                        success = 1;
                        break;
                    } else {
                        log_message("Connection check failed after dial");
                    }
                }
                
                if (retries < MAX_RETRIES) {
                    log_message("Waiting %d seconds before next retry", RECONNECT_DELAY);
                    sleep(RECONNECT_DELAY);
                }
            }
            
            if (!success) {
                log_message("Failed to reconnect after %d attempts", MAX_RETRIES);
            }
        }
        
        // 检查间隔
        sleep(CHECK_INTERVAL);
    }
    
    // 清理工作
    ppp_hangup();
    unlink(PID_FILE);
    log_message("PPP Manager stopped");
    
    return 0;
}
七、总结

        本文详细介绍了在 Linux 嵌入式系统中实现 PPP 拨号的方法,包括 PPP 协议基础、系统架构、编程实现方案、配置文件说明以及调试技巧等内容。通过调用系统命令或直接操作 PPP 库函数,可以实现可靠的 PPP 拨号程序。

        在实际开发中,应根据具体需求选择合适的实现方案,并注意错误处理和断线重连等机制的实现,以提高系统的稳定性和可靠性。增强版的 PPP 拨号管理程序提供了更完善的功能,包括配置文件读取、守护进程运行、网络状态监控和自动重连等特性,可作为实际项目的参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2400699.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UE 材质基础第三天

飘动的旗帜 错乱的贴图排序&#xff0c;创建一个材质函数 可以用在地面材质 体积云材质制作 通过网盘分享的文件&#xff1a;虚幻引擎材质宝典.rar 链接: https://pan.baidu.com/s/1AYRz2V5zQFaitNPA5_JbJw 提取码: cz1q --来自百度网盘超级会员v6的分享

【Github/Gitee Webhook触发自动部署-Jenkins】

Github/Gitee Webhook触发自动部署-Jenkins #mermaid-svg-hRyAcESlyk5R2rDn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hRyAcESlyk5R2rDn .error-icon{fill:#552222;}#mermaid-svg-hRyAcESlyk5R2rDn .error-tex…

新松机械臂 2001端口服务的客户端例程

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

电脑网络重置,找不到原先自家的WIFI,手机还能正常连接并上网

问题排查&#xff1a;1、电脑感觉网络太慢&#xff0c;因此打算点击了网络重置 2、点击提示会删除网络&#xff0c;在五分钟后关机重启 3、从设备管理器设备的无线wifi属性-事件中发现删除记录 4、选择更新驱动程序 5、从列表中选取 6、更改回老驱动版本 备选方案&#…

期末复习(学习)之机器学习入门基础

上课没听过报道。欢迎补充交流&#xff01; 前言&#xff1a;老师画的重点其实可以完全不用看&#xff0c;我这里只是看了一眼书顺着书本敲一遍。 比较干货的部分&#xff0c;直接看学习通的内容就好。最重要的是把学习通的内容记好。 目录 老师划的重点&#xff1a;P50 结构…

网络各类型(BMA,NBMA,P2P)

网络类型—基于二层&#xff08;数据链路层&#xff09;使用的协议不同从而导致数据包封装方式不同&#xff0c;工作方式也有所区别&#xff0c;从而对网络本身进行分类 一、网络类型分类 2. 关键差异对比 1. HDLC&#xff08;高级数据链路控制协议&#xff09; 协议特点&…

【计算机网络】第3章:传输层—概述、多路复用与解复用、UDP

目录 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输&#xff1a;UDP 四、总结 &#xff08;一&#xff09;多路复用与解复用 &#xff08;二&#xff09;UDP 一、概述和传输层服务 二、多路复用与解复用 三、无连接传输&#xff1a;UDP 四、总结 &#xff08…

神经符号AI的企业应用:结合符号推理与深度学习的混合智能

&#x1f4a1; 技术前沿&#xff1a; 神经符号AI代表了人工智能发展的新阶段&#xff0c;它将深度学习的模式识别能力与符号推理的逻辑分析能力有机结合&#xff0c;创造出更加智能、可解释且可靠的AI系统。这种混合智能技术正在重塑企业的智能化应用&#xff0c;从自动化决策到…

VSCode 中 C/C++ 安装、配置、使用全攻略:小白入门指南

引言 本文为Windows系统下安装配置与使用VSCode编写C/C代码的完整攻略&#xff0c;示例机器为Windows11。 通过本文的指导&#xff0c;你可以成功在Windows 机器上上使用VSCode进行C/C开发。 在文章开始之前&#xff0c;你可以先阅读下面这段话&#xff0c;以便于对步骤有个大…

重温经典算法——希尔排序

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 基本原理 希尔排序是插入排序的改进版&#xff0c;通过按增量分组并逐步缩小增量实现排序。时间复杂度取决于增量序列&#xff0c;平均约为 O(n log n) 到 O(n^(3/2))&…

CortexON:开源的多代理AI系统无缝自动化和简化日常任务

简介 CortexON是一个开源的多代理AI系统&#xff0c;灵感来自Manus和OpenAI DeepResearch等高级代理平台。CortexON旨在无缝自动化和简化日常任务&#xff0c;擅长执行复杂的工作流程&#xff0c;包括全面的研究任务、技术操作和复杂的业务流程自动化。 技术架构 CortexON的技…

海信IP810N-海思MV320芯片-安卓9-2+16G-免拆优盘卡刷固件包

海信IP810N-海思MV320芯片-安卓9-216G-免拆优盘卡刷固件包 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 1.准备一个优盘&#xff0c;最佳是4G&#xff0c;卡刷强刷刷机&#xff0c;用一个usb2.0的8G以下U盘&#xff0c;fat32&#xff0c;2048块单分区格式化…

2025年6月4日收获

Authorization Authorization是一种通用的、标准化的权限控制和认证的通用框架&#xff0c;它能够使跨系统和跨域的身份验证和授权管理更容易&#xff0c;使不同应用程序之间能够更轻松地实现单点登录&#xff08;SSO&#xff09;、用户身份验证和授权控制等。 在前端使用 axi…

leetcode hot100 链表(二)

书接上回&#xff1a; leetcode hot100 链表&#xff08;一&#xff09;-CSDN博客 8.删除链表的倒数第N个结点 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* currhead;int len0;while(curr){currcurr->next;len;}int poslen-n…

6. MySQL基本查询

1. 表的增删改查 Create(创建), Retrieve(读取), Update(更新), Delete(删除) 2. Create & Insert 语法: insert [info] table_name () values () 2.1. 案例: 创建一个学生表 指定列单行插入, 如果values前省略, 则默认是全属性插入多行指定列插入, 中间分隔符为, 3. 插入替…

CMS32M65xx/67xx系列CoreMark跑分测试

CMS32M65xx/67xx系列CoreMark跑分测试 1、参考资料准备 1.1、STM32官方跑分链接 1.2、官网链接 官方移植文档&#xff0c;如下所示&#xff0c;点击红框处-移植文档: A new whitepaper and video explain how to port CoreMark-Pro to bare-metal 1.3、测试软件git下载链接 …

中国区域30m/15天植被覆盖度数据集(2010-2022)

时间分辨率&#xff1a;日空间分辨率&#xff1b;&#xff1a;10m - 100m共享方&#xff1a;式开放获取数据大小&#xff1a;2.98 TB数据时间范围&#xff1a;2010-01-01 — 2022-12-31元数据更新时间&#xff1a;2024-12-23 数据集摘要 高时空分辨率的植被覆盖度产品存在着广…

力扣HOT100之二分查找:74. 搜索二维矩阵

这道题直接a了&#xff0c;我们可以参考上一道题&#xff1a;35.搜索插入位置的思路&#xff0c;详情见我的上一篇博客。将每一行的第一个元素当作一个数组中的元素&#xff0c;然后对这个数组进行二分查找&#xff0c;如果直接找到了target&#xff0c;则直接返回true&#xf…

编程技能:格式化打印04,sprintf

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;格式化打印03&#xff0c;printf 回到目录…

R语言基础| 下载、安装

在此前的单细胞教程中&#xff0c;许多小伙伴都曾因为R语言基础不足而十分苦恼。R语言是一种开源的编程语言和软件环境&#xff0c;专门用于统计分析、图形表示和数据挖掘。它最初由Ross Ihaka和Robert Gentleman在1993年创建&#xff0c;旨在为统计学家和数据分析师提供一个广…