当前位置: 首页 > article >正文

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

一、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或ERRORif (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];// 设置APNsnprintf(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;}// 配置GPRSif (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;// 第一步forkpid = fork();if (pid < 0) {exit(EXIT_FAILURE);}if (pid > 0) {exit(EXIT_SUCCESS);  // 父进程退出}// 创建新会话sid = setsid();if (sid < 0) {exit(EXIT_FAILURE);}// 第二步forkpid = 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 拨号管理程序提供了更完善的功能,包括配置文件读取、守护进程运行、网络状态监控和自动重连等特性,可作为实际项目的参考。

相关文章:

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

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

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…...

软件工程专业本科毕业论文模板

以下是软件工程专业本科毕业论文的通用模板框架&#xff0c;结合学术规范与工程实践要求&#xff0c;涵盖从需求分析到测试验证的全流程结构&#xff0c;并附格式说明与写作建议&#xff1a; 一、前置部分 1. 封面 - 包含论文标题&#xff08;简明反映研究核心&#xff0c;如“…...

新松机械臂 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; 协议特点&…...

Linux 库文件的查看和管理

Linux 库文件说明1、库文件的类型2、库文件存储路径3、库文件查找顺序 Linux 库文件管理1、查看动态库相关信息2、添加动态库查找路径 Linux 库文件说明 1、库文件的类型 Linux 中的库文件本质上就是封装好的功能模块&#xff0c;某个应用程序如果要实现某个功能&#xff0c;…...

Java设计模式深度解析:策略模式的核心原理与实战应用

目录 策略模式基础解析策略模式实现指南策略模式典型应用场景Java生态中的策略模式实践策略模式进阶技巧策略模式最佳实践总结与展望1. 策略模式基础解析 1.1 核心概念与定义 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法族,将每个算法封装成独立…...

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

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

6、在树莓派上安装 NTP(Network Time Protocol )服务的步骤

在树莓派上安装 NTP&#xff08;Network Time Protocol &#xff09;服务的步骤&#xff1a; 1. 安装 NTP 服务 打开树莓派终端&#xff0c;输入以下命令更新软件包列表&#xff1a; sudo apt-get update然后安装 NTP 服务&#xff1a; sudo apt-get install ntp2. 配置 NT…...

神经符号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块单分区格式化…...

【Golang】使用gin框架导出excel和csv文件

目录 1、背景2、go库【1】excel库下载【2】csv标准库 3、代码示例4、使用方法 1、背景 项目中可能会遇到导入导出一批数据的功能&#xff0c;对于批量大数据可能用表格的方式直观性更好&#xff0c;所以本篇文件来讲一下go中导出excel和csv文件的方式。 2、go库 【1】excel库…...

【unity游戏开发入门到精通——通用篇】AssetBundle(AB包)和AssetBundleBrowser的使用介绍

文章目录 前言1、什么是AssetBundle?2、AB包与Resources系统对比3、AB包核心价值一、AB包打包工具Asset Bundle Browser1、下载安装AssetBundles-Browser2、打开Asset Bundle Browser窗口3、如何让资源关联AB包二、AssetBundleBrowser参数相关1、Configure 配置页签2、Build 构…...

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. 插入替…...

JavaWeb简介

目录 1.1 JavaWeb 简介​​ ​​1.2 JavaWeb 技术栈​​ ​​1.3 JavaWeb 交互模式​​ ​​1.4 JavaWeb 的 C/S 和 B/S 模式​​ ​​C/S 模式 (Client-Server / 客户端-服务器模式)​​ ​​B/S 模式 (Browser-Server / 浏览器-服务器模式)​​ ​​1.5 JavaWeb 实现前…...

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 数据集摘要 高时空分辨率的植被覆盖度产品存在着广…...

LabVIEW准分子激光器智能控制系统

LabVIEW 开发准分子激光器智能控制系统&#xff0c;针对放电激励型准分子激光器强电磁干扰环境下的控制难题&#xff0c;采用 “PC 端 LabVIEW 人机交互 MCU 端实时控制 光纤隔离通信” 架构&#xff0c;实现激光能量闭环控制、腔体环境监测、气路自动管理等功能。硬件选用 N…...

微服务面试资料1

在当今快速发展的技术领域&#xff0c;微服务架构已经成为构建复杂系统的重要方式之一。本文将围绕微服务的核心概念、技术栈、分布式事务处理、微服务拆分与设计&#xff0c;以及敏捷开发实践等关键问题展开深入探讨&#xff0c;旨在为准备面试的 Java 开发者提供一份全面的复…...

Pytest Fixture 详解

Pytest Fixture 详解 Fixture 是 pytest 最强大的功能之一&#xff0c;用于提供测试所需的依赖资源&#xff08;如数据库连接、临时文件、模拟对象等&#xff09;&#xff0c;并支持复用、作用域控制和自动清理。以下是全面详解&#xff1a; 1. 基本用法 定义 Fixture 使用 …...

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

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

【前端】前后端通信

前端开发主要完成的两件事&#xff1a; 1&#xff09;界面搭建 2&#xff09;数据交互 本知识页参考&#xff1a; https://juejin.cn/post/6925296067378429960 0. XMLHttpRequest 客户端的一个API&#xff0c;为浏览器和服务器通信提供了一个便携通道。现代浏览器支持XMLHttp…...