C中对 printf("%.*s/n",len,content) 的意思理解。

本文详细解释了C语言中printf函数的格式控制用法,特别是%.*s格式符的应用,展示了如何通过指定宽度来精确控制输出字符串的长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一直不明白是什么意思,问了别人才知道printf("%.*s/n",6,"aaaaaaaaa")是什么意思。
如:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
        char *content = "abcdefghijklmn";
        printf("输出数据 :%.*s/n",5,content);
        printf("输出数据 :%.*s/n",strlen(content),content);
        return 0;
}

%.*s 指得是输入/出是一个指针参数。
5 表示输入/出指字符数据的长度,如果 5 等当前内容的长度,全部输出,如果小于内容长度,那么只输出前5个内容。

 

打印结果如下:

 

css22-test$ a.out
输出数据 :abcde
输出数据 :abcdefghijklmn

 

请帮我填充开发报告。1. 前言 1.1. 项目简要说明 本项目。 在此简要描述项目背景、需求。可参考立项报告或软件开发任务书,进行概括或补充。项目一般对应机型,如WR741N 1.0。 1.2. 任务概述 说明本任务是项目的全部,还是项目的子模块,是新设计模块,还是对原有模块或代码的修改,等等。任务一般对应模块,如WR741N 1.0的防火墙模块。 1.3. 可用资源 可选,说明不需要重新开发的可利用资源,如现成的模块、控件和软件等。 1.4. 术语定义 列举本文所用的专门术语的定义和英文缩写词的原文及解释。注意,术语之间如有引用关系,被引用者列在前面。 1.5. 参考资料 参考资料指概要设计中引用的开发流程文档或规范,或者有利于加深概要设计理解的资料(读者必须能容易获得)。请在下面列出资料,并给出这些文件资料的标题、作者、编号、版本号、发表日期和出版单位或资料的来源:  经批准的开发文档;  引用的软件开发标准和规范;  说明本文档中引用的其他文件和资料。 2. 需求分析 强烈鼓励单独撰写需求分析文档。 如果没有单独的需求分析文档,在此分层次对需求进行分析。主要是分析以下方面:  界定系统的对外提供的功能(将对应于概要设计中的模块接口或系统界面);  详细的功能项及功能的分类(将对应于概要设计中的模块划分);  功能分类之间的关系(将对应于概要设计中的模块关系);  功能的作用或流程(将对应于概要设计中的模块内部建模);  提供上述功能时需要达到的性能(将影响概要设计中协议、算法等的选择)。 3. 原理概述 本章分析任务的重点原理,如协议、算法等等。如果需要原理概述,本章为概要设计的重点内容。不需要时删除本章。 注意,原理不是实现的结果,而是实现的依据。如果属于协议设计,要说明为什么要这样设计。移植型的任务,原理着重讲移植要注意的平台差异。修改/增加型的任务,原理着重讲修改或增加的部分。 以下小节如果内容较多,请扩展成独立的一章。 3.1. 协议分析(或设计) 本节分析要使用并实现的协议标准(如PPTP协议),或根据需求设计新协议(如防火墙产品用户认证协议)及设计的出发点和思路。不需要时删除本节。 3.1.1. 协议描述 概述协议实现的目标、原理等。 3.1.2. 协议数据格式 协议使用的数据格式,如网络网络协议的帧格式。 3.1.3. 协议描述 描述协议的以下方面:  协议收发包的方式,如使用socket还是其他接口,是否阻塞,等等。  协议模块与任务(或线程)的关系,如果涉及多任务,说明任务间通信的方式。  双方或多方进行协议通信的互交图、状态图或其他形式的描述。 3.2. 重要算法与数据结构 本节分析要实现的重要算法(如防火墙项目策略模块查找算法设计)、相关数据定义以及算法依赖的数据结构。 4. 系统构架描述 4.1. 概述 概要地叙述模块划分的原则,如通过对需求进行分析得到几类功能,从而相应地将系统分成几个模块。 注意:这里说的模块,指的是本任务之下划分的模块。如果本任务是产品的一个模块,那么这里的模块实际上是产品模块中的子模块。 4.2. 模块结构 辅于模块结构图等形式,表述模块的划分情况(根据、命名、结果)和模块之间的关系,说明模块的相互关系是本节的重点。 说明:这是设计中对复杂系统“分而治之”的过程。注意“分”的合理性,并通过对模块关系的分析,保持系统的功能完整性。 4.3. 模块描述和建模 逐一说明每个模块的基本情况,包括:模块自身的功能;对项目以内或以外其他模块提供的功能;模块的简要流程或算法的名称;模块的重要性;等等。 如果划分的模块有比较复杂的(如存在多个对象),则对模块进行建模:  首先介绍模块及其包括对象的功能和属性。  其次可以描述对象之间的关系,如成绩管理模块中,学生、成绩、班级等对象的关系。  然后可以描述模块的主要流程,如加密过程。 5. 任务(或进程/线程)设计 必须描述的内容。 如果本任务需要单独的任务(或进程/线程)或采用多任务(进程/线程)、线程池等,在本章描述任务或进程/线程的使用设计(包括它们与模块的关系)。即使没有增加新的任务或进程/线程,也要描述代码的运行方式。如果涉及中断处理的,同样要说明。 5.1. 原因 说明使用新的任务(或进程/线程)、采用多个任务(或进程/线程)或线程池的原因。 5.2. 任务(或进程/线程)内部流程 依次说明每个任务/进程/线程实现什么流程,如果前面已经有流程,指出对应关系即可。 5.3. 任务(或进程/线程)间通信 每两个需要进行信息交互的任务,都要说明通信的信息内容、交互方式。 6. 内存管理 如果需要内存的特别使用和管理,在本章说明,否则删除本章。 6.1. 说明 说明哪些数据类型需要进行特别管理,如:  作用  被哪些模块、函数、任务(或线程)使用 6.2. 设计 如何对内存进行管理,包括但不限于以下内容:  是否要互斥和/或同步  是否要反复申请、释放  内存维护方式 7. 出错处理 可选。复杂项目需要统一的出错处理,否则删除本节。 8. 用户界面概要设计 可选,如有用户界面(包括GUI和CLI),在本章描述界面设计,否则删除本章节。先说明界面的关系,再对每个主要界面进行说明。 8.1. 界面组织 说明界面设计的指导原则。用图表描述不同界面间的组织关系。 8.2. 界面设计 对主要界面进行设计。 9. 接口概要设计 本章的接口指提供给其他模块调用的接口。如果需要给其他模块或对外提供接口,在本章说明,否则删除本章。 接口的设计要与使用模块的设计人进行协商。要注意完整性(能实现所有需要的功能)和易用性(不能让使用者需要经过复杂的准备或步骤才能调用)。 9.1. 概述 说明接口设计思想。要说明是否达到完整性,给出充分理由。 9.2. 接口分类与功能 接口设计,包括接口的分类及功能说明,大致的输入和输出,等等。如果接口提供给不同模块使用,或接口分不同的功能,要进行分类并说明。 如果一些对外功能需要调用若干个接口的组合才能实际,请说明。 10. 开发环境、测试环境及部署环境 10.1. 开发环境 指明开发所需环境,如:  硬件(交叉编译时包括宿主机和目标机)  OS(交叉编译时包括宿主机和目标机)  编译器、集成开发环境  其他 10.2. 测试环境 测试所需环境。如网络拓扑、测试设备等。 10.3. 部署环境 开发成果的部署环境。包括需要使用的硬件、软件、网络环境等。注意部署的层次性,如DHCP Client模块,部署到vxWorks平台的无线路由器(在WAN口使用),再描述无线路由器的部署。 《简要描述即可》项目具体如下,分别使用多线程,多进程,IO多路复用实现一个简单的web server(端口80)承载附件的web。对比这三种实现方式各自的优缺点。 下面给出了process多进程服务器的代码样例,其余两个改动不大。/* Copyright(c) Lianzhou International Co.Ltd. * * file process_server.c * brief This is a simple muti-process server. * * author Sun Haoming * version 1.0.0 * date 25Aug14 * * history * \arg 1.0.0, 25Aug14, Sun Haoming, Create the file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/stat.h> #include <fcntl.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define PORT 80 #define BUFFER_SIZE 2048 /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* EXTERN_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ const char *get_content_type(const char *path); void send_response(int client_fd, const char *path); /**************************************************************************************************/ /* VARIABLES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /* 根据拓展名返回类型 */ const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); /* strrchr从末尾查找至. */ if (ext) { if (strcmp(ext, ".html") == 0) return "text/html"; if (strcmp(ext, ".css") == 0) return "text/css"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".jpg") == 0) return "image/jpeg"; } return "application/octet-stream"; /*默认返回二进制流*/ } /* 发送HTTP响应给客户端 */ void send_response(int client_fd, const char *path) { char header[BUFFER_SIZE]; /*响应头buffer*/ char buffer[BUFFER_SIZE]; /*内容buffer*/ struct stat st; /*文件状态*/ /* 文件不存在 */ if (stat(path, &st) == -1) { printf("该文件未找到:%s\n", path); /* 404 文件头 */ snprintf(header, BUFFER_SIZE, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html\r\n" "\r\n" "<h1>404 Not Found</h1>"); send(client_fd, header, strlen(header), 0); return; } /*读取文件*/ int fd = open(path, O_RDONLY); if (fd < 0) { printf("打开该文件失败: %s\n", path); return; } /* 200 OK 文件头 */ snprintf(header, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "\r\n", get_content_type(path), st.st_size); send(client_fd, header, strlen(header), 0); printf("发送文件: %s (大小: %ld 字节)\n", path, st.st_size); /*发送给客户端*/ ssize_t bytes_read; while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) { send(client_fd, buffer, bytes_read, 0); } close(fd); } /*客户端请求线程*/ void handle_client(int client_fd) { /*直接接受int*/ char buffer[BUFFER_SIZE]; char path[BUFFER_SIZE] = "."; /* 默认./ */ char method[16] = {0}; /*存储请求方法*/ int content_length = 0; /* POST内容长度*/ char request_path[BUFFER_SIZE] = {0}; /* 存储原始请求路径*/ /*读取客户端请求*/ ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read <= 0) { printf("读取客户端请求失败\n"); close(client_fd); return; } buffer[bytes_read] = '\0'; /* 获取客户端IP */ struct sockaddr_in addr;/*地址结构*/ socklen_t addr_len = sizeof(addr);/*地址长度*/ getpeername(client_fd, (struct sockaddr*)&addr, &addr_len);/*获取当前socket的对端地址*/ char client_ip[INET_ADDRSTRLEN];/*存储IP*/ inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip));/*将二进制IP转为字符串*/ printf("收到来自 %s 的请求:\n%.*s\n", client_ip, (int)bytes_read, buffer); /* 解析请求方法 */ sscanf(buffer, "%s %s", method, request_path); /* 获取GET请求 */ if (strcmp(method, "GET") == 0) { /* 根目录请求*/ if (strcmp(request_path, "/") == 0) { strcat(path, "/Index.html"); } else { strncat(path, request_path, BUFFER_SIZE - strlen(path) - 1); } send_response(client_fd, path); } /* 处理POST请求 */ else if (strcmp(method, "POST") == 0) { char *body_start = strstr(buffer, "\r\n\r\n"); if (body_start) { body_start += 4; char *content_len_ptr = strstr(buffer, "Content-Length: "); if (content_len_ptr) { sscanf(content_len_ptr, "Content-Length: %d", &content_length); } /*处理表单提交*/ if (strstr(path, "/submit") != NULL) { char response[BUFFER_SIZE]; snprintf(response, BUFFER_SIZE, "HTTP/1.1 200 OK\r\n" "Content-Type: text/html\r\n\r\n" "<html><body>" "<h1>表单已提交</h1>" "<p>收到数据: %.*s</p>" "</body></html>", content_length, body_start); send(client_fd, response, strlen(response), 0); printf("处理POST数据: %.*s\n", content_length, body_start); } } } /* 其他请求方法*/ else { const char *resp = "HTTP/1.1 405 Method Not Allowed\r\n\r\n"; send(client_fd, resp, strlen(resp), 0); } close(client_fd); printf("请求处理完成\n"); return; } int main() { signal(SIGPIPE, SIG_IGN); /*忽略SIGPIPE管道信号*/ printf("Web服务器启动,监听端口 %d \n", PORT); /*创建TCP套接字*/ int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd < 0) { perror("创建套接字失败"); exit(EXIT_FAILURE); } /*允许快速重启,地址重用*/ int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /*配置服务器地址结构*/ struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = INADDR_ANY }; /* 绑定 */ if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("绑定端口失败"); exit(EXIT_FAILURE); } /* 监听 */ if (listen(server_fd, 10) < 0) { perror("监听失败"); exit(EXIT_FAILURE); } printf("服务器已启动\n"); /* 持续接收客户端连接*/ while (1) { struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); /*阻塞等到下一个客户端连接*/ int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); if (client_fd < 0) { perror("接受连接失败"); continue; } /*非阻塞的回收僵尸进程,WNOHANG无子进程退出立即返回*/ while (waitpid(-1, NULL, WNOHANG) > 0); /*创建子进程处理客户端请求*/ pid_t pid = fork(); if (pid < 0) { perror("fork失败"); close(client_fd); } else if (pid == 0) { close(server_fd);/*子进程不用监听服务端*/ handle_client(client_fd); /*子进程处理客户端请求*/ exit(0); } else { close(client_fd); /*父进程不要客户端*/ } } close(server_fd); return 0; }
08-16
你是一个编程小白,拿到了以下的代码,如何从0基础逐渐理解以下代码的功能和实现方法:/* Copyright(c) 2025-2025 Chengdu TP-Link Technologies Co.Ltd. * * file httpdServer.c * brief This is a simple web server, supports multi-program, multi-thread and multi-IO, and supports operation Get and Post. * * author Zhang Jiachong * version 1.0.0 * date 01-08-25 * * history \arg 1.0.0, 01-08-25, Zhang Jiachong, Create the file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <netinet/in.h> #include <dirent.h> #include <signal.h> #include <fcntl.h> #include <ctype.h> #include <wait.h> #include <sys/epoll.h> /**************************************************************************************************/ /* DEFINES */ #define PORT 80 #define WEB_ROOT "./web" #define THREAD_POOL_SIZE 4 #define MAX_EVENTS 100 /**************************************************************************************************/ /**************************************************************************************************/ /* TYPES */ enum method_enum { MULTI_PROG, /* Multi-process mode */ MULTI_THRD, /* Multi-thread mode */ MULTI_IO, /* I/O multiplexing mode */ THRD_POLL /* Thread pool mode */ }; typedef struct { void *(*task_function)(void *); /* Function pointer for request handling */ int client_fd; } Task; typedef struct { pthread_t *threads; /*Worker threads array*/ Task *task_queue; /*Task queue buffer*/ int queue_size; int queue_capacity; int head; int tail; int count; pthread_mutex_t lock; pthread_cond_t task_cond; /*Condition for task availability*/ pthread_cond_t space_cond; /*Condition for queue space*/ } ThreadPool; /**************************************************************************************************/ /**************************************************************************************************/ /* EXTERN_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ /**************************************************************************************************/ /* VARIABLES */ int method = THRD_POLL; /**************************************************************************************************/ /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ static void set_nonblocking(int fd); static void thread_pool_init(ThreadPool *pool, int size, int capacity); static void *worker_thread(void *arg); static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd); static char *url_decode(const char *str); static void log_request(const char *method, const char *path, int status, const char *status_msg); static int send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len); static void *handle_request(void *arg); /**************************************************************************************************/ /**************************************************************************************************/ /* PUBLIC_FUNCTIONS */ /**************************************************************************************************/ static void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); } /* * fn static void thread_pool_init(ThreadPool *pool, int size, int capacity) * brief The thread pool initialization function. * * param[in] pool The thread pool. * param[in] size The number of threads in the thread pool. * param[in] capacity The maximum number of tasks in the thread pool. */ static void thread_pool_init(ThreadPool *pool, int size, int capacity) { pool->threads = malloc(size * sizeof(pthread_t)); pool->task_queue = malloc(capacity * sizeof(Task)); pool->queue_size = 0; pool->queue_capacity = capacity; pool->head = 0; pool->tail = 0; pool->count = 0; pthread_mutex_init(&pool->lock, NULL); pthread_cond_init(&pool->task_cond, NULL); pthread_cond_init(&pool->space_cond, NULL); for (int i = 0; i < size; i++) { pthread_create(&pool->threads[i], NULL, (void *)worker_thread, pool); } } /* * fn static void *worker_thread(void *arg) * brief The working function of a thread pool thread. * * param[in] arg The thread pool. */ static void *worker_thread(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { pthread_mutex_lock(&pool->lock); /* Wait for tasks if queue empty */ while (pool->queue_size == 0) { pthread_cond_wait(&pool->task_cond, &pool->lock); } Task task = pool->task_queue[pool->head]; pool->head = (pool->head + 1) % pool->queue_capacity; pool->queue_size--; pthread_cond_signal(&pool->space_cond); pthread_mutex_unlock(&pool->lock); int *sock_ptr = malloc(sizeof(int)); *sock_ptr = task.client_fd; task.task_function(sock_ptr); } return NULL; } /* * fn static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd) * brief Add a new task to the task queue in the thread pool. * * param[in] pool The thread pool. * param[in] task_function The function bound to the new task. * param[in] client_fd The socket for browser clients. */ static void add_task(ThreadPool *pool, void *(*task_function)(void *), int client_fd) { pthread_mutex_lock(&pool->lock); /* Wait for tasks if queue empty */ while (pool->queue_size == pool->queue_capacity) { pthread_cond_wait(&pool->space_cond, &pool->lock); } pool->task_queue[pool->tail].task_function = task_function; pool->task_queue[pool->tail].client_fd = client_fd; pool->tail = (pool->tail + 1) % pool->queue_capacity; pool->queue_size++; pthread_cond_signal(&pool->task_cond); pthread_mutex_unlock(&pool->lock); } /* * fn static char *url_decode(const char *str) * brief Parses the URL in the Post. * * param[in] input_int The URL in the Post. * * return the parsesd result */ static char *url_decode(const char *str_in) { char *output = malloc(strlen(str_in) + 1); if (!output) return NULL; /* (% encoding and + replacement handling) */ char *dst = output; while (*str_in) { if (*str_in == '+') { *dst++ = ' '; str_in++; } else if (*str_in == '%' && isxdigit(str_in[1]) && isxdigit(str_in[2])) { char hex[3] = {str_in[1], str_in[2], '\0'}; *dst++ = (char)strtol(hex, NULL, 16); str_in += 3; } else { *dst++ = *str_in++; } } *dst = '\0'; return output; } static void log_request(const char *method, const char *path, int status, const char *status_msg) { printf("[%s] %s -> %d %s\n", method, path, status, status_msg); } /* * fn static void send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len) * brief Send a response to the browser. * * param[in] sock Client socket. * param[in] status Response status. * param[in] status_msg Meaning of response status. * param[in] content_type Type of body. * param[in] body Data to be send. * param[in] body_len Length of body. * * return the send result */ static int send_response(int sock, int status, const char *status_msg, const char *content_type, const char *body, int body_len) { char header[1024]; /* Build HTTP response header */ snprintf(header, sizeof(header), "HTTP/1.1 %d %s\r\n" "Content-Type: %s\r\n" "Content-Length: %d\r\n" "Connection: close\r\n\r\n", status, status_msg, content_type, body_len); size_t result = 0; result = send(sock, header, strlen(header), 0); if (result == -1) { close(sock); return -1; } if (body && body_len > 0) { send(sock, body, body_len, 0); if (result == -1) { close(sock); return -1; } } return 0; } /* * fn static void *handle_request(void *arg) * brief Handle the request from browser. * * param[in] arg Pointer of the client socket. * * return meaningless */ static void *handle_request(void *arg) { int sock = *(int *)arg; free(arg); char buffer[4096]; size_t bytes_read = recv(sock, buffer, sizeof(buffer) - 1, 0); if (bytes_read <= 0) { close(sock); return NULL; } buffer[bytes_read] = '\0'; char method[16], path[256]; sscanf(buffer, "%s %s", method, path); if (strcmp(method, "GET") == 0) { char filepath[512]; if (strcmp(path, "/") == 0) { strcpy(path, "/Index.html"); } sprintf(filepath, "%s%s", WEB_ROOT, path); FILE *file = fopen(filepath, "rb"); if (file) { fseek(file, 0, SEEK_END); long len = ftell(file); fseek(file, 0, SEEK_SET); char *content = malloc(len); fread(content, 1, len, file); fclose(file); const char *content_type = "text/html"; if (strstr(path, ".css")) { content_type = "text/css"; } else if (strstr(path, ".js")) { content_type = "application/json"; } else if (strstr(path, ".png")) { content_type = "image/png"; } else if (strstr(path, ".jpg")) { content_type = "image/jpeg"; } send_response(sock, 200, "OK", content_type, content, len); free(content); log_request(method, path, 200, "OK"); } else { const char *not_found = "<h1>404 Not Found</h1>"; send_response(sock, 404, "Not Found", "text/html", not_found, strlen(not_found)); log_request(method, path, 404, "Not Found"); } } else if (strcmp(method, "POST") == 0) { char *content_length_ptr = strstr(buffer, "Content-Length: "); int content_length = 0; if (content_length_ptr) { sscanf(content_length_ptr + 16, "%d", &content_length); } if (content_length <= 0) { const char *bad_request = "<h1>400 Bad Request: Missing Content-Length</h1>"; send_response(sock, 400, "Bad Request", "text/html", bad_request, strlen(bad_request)); log_request(method, path, 400, "Bad Request"); close(sock); return NULL; } char *post_data = malloc(content_length + 1); if (!post_data) { const char *server_error = "<h1>500 Internal Server Error</h1>"; send_response(sock, 500, "Server Error", "text/html", server_error, strlen(server_error)); log_request(method, path, 500, "Server Error"); close(sock); return NULL; } char *body_start = strstr(buffer, "\r\n\r\n"); if (body_start) { body_start += 4; memcpy(post_data, body_start, content_length); } else { const char *bad_request = "<h1>400 Bad Request: Bad Message Format</h1>"; send_response(sock, 400, "Bad Request", "text/html", bad_request, strlen(bad_request)); log_request(method, path, 400, "Bad Request"); close(sock); return NULL; } post_data[content_length] = '\0'; /* post_data : name=z111&email=125253455%40qq.com&message=dasdsasd */ if (strcmp(path, "/data/contact.json") == 0) { char name[128] = {0}; char email[128] = {0}; char message[512] = {0}; char *token = strtok(post_data, "&"); while (token != NULL) { char *eq = strchr(token, '='); if (eq) { *eq = '\0'; char *key = token; char *value = url_decode(eq + 1); if (strcmp(key, "name") == 0) { strncpy(name, value, sizeof(name) - 1); } else if (strcmp(key, "email") == 0) { strncpy(email, value, sizeof(email) - 1); } else if (strcmp(key, "message") == 0) { strncpy(message, value, sizeof(message) - 1); } free(value); } token = strtok(NULL, "&"); } char response[1024]; snprintf(response, sizeof(response), "{" "\"callback\": \"Message received successfully\"," "\"details\": {" "\"name\": %s," "\"email\": %s," "\"message\": %s" "}" "}", name, email, message); send_response(sock, 200, "OK", "application/json", response, strlen(response)); log_request(method, path, 200, "OK"); } else { const char *not_implemented = "<h1>501 Not Implemented</h1>"; send_response(sock, 501, "Not Implemented", "text/html", not_implemented, strlen(not_implemented)); log_request(method, path, 501, "Not Implemented"); close(sock); return NULL; } free(post_data); } else { const char *not_implemented = "<h1>501 Not Implemented</h1>"; send_response(sock, 501, "Not Implemented", "text/html", not_implemented, strlen(not_implemented)); log_request(method, path, 501, "Not Implemented"); close(sock); return NULL; } close(sock); return NULL; } int main() { /* 当client先终止连接,server向socket中写文件时会触发SIGPIPE信号导致程序退出 */ /* 此处忽略SIGPIPE信号,在send_response中做处理 */ signal(SIGPIPE, SIG_IGN); int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == server_fd) { return -1; } int opt = 1; setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(PORT), .sin_addr.s_addr = htonl(INADDR_ANY)}; bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); listen(server_fd, 10); printf("Web server running on port %d\n", PORT); if (MULTI_IO == method || THRD_POLL == method) { int epoll_fd = epoll_create1(0); struct epoll_event ev, events[MAX_EVENTS]; ev.events = EPOLLIN; ev.data.fd = server_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev); ThreadPool thread_pool; if (THRD_POLL == method) { thread_pool_init(&thread_pool, THREAD_POOL_SIZE, 256); } while (1) { int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); for (int i = 0; i < n; i++) { if (events[i].data.fd == server_fd) { int client_fd = accept(server_fd, NULL, NULL); if (THRD_POLL == method) { set_nonblocking(client_fd); } ev.events = EPOLLIN | EPOLLET; ev.data.fd = client_fd; epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev); } else { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL); if (THRD_POLL == method) { add_task(&thread_pool, handle_request, events[i].data.fd); } else { int *sock_ptr = malloc(sizeof(int)); *sock_ptr = events[i].data.fd; handle_request(sock_ptr); } } } } } else { while (1) { int client_fd = accept(server_fd, NULL, NULL); int *sock_ptr = malloc(sizeof(int)); *sock_ptr = client_fd; if (MULTI_PROG == method) { pid_t pid = fork(); if (pid == 0) { close(server_fd); handle_request(sock_ptr); exit(0); } else { close(client_fd); waitpid(-1, NULL, WNOHANG); } } else if (MULTI_THRD == method) { pthread_t tid; pthread_create(&tid, NULL, handle_request, sock_ptr); pthread_detach(tid); } } } close(server_fd); return 0; }
08-09
. ├── bin │ ├── common.o │ ├── epoll_serv │ ├── epoll_serv.o │ ├── process_serv │ ├── process_serv.o │ ├── pthread_serv │ └── pthread_serv.o ├── include │ └── common.h ├── Makefile ├── src │ ├── common.c │ ├── epoll_serv.c │ ├── process_serv.c │ └── pthread_serv.c └── web ├── About.html ├── Contact.html ├── css │ ├── reset.css │ ├── style.css │ └── style_mobile.css ├── data │ └── contact.json ├── images │ ├── About_baby.png │ ├── About_ball.png │ ├── About_be.png │ ├── About_bird.png │ ├── About_content.png │ ├── About_message.png │ ├── About_@.png │ ├── body.png │ ├── footer_baby.png │ ├── footer_ball.png │ ├── footer_be.png │ ├── footer_bird.png │ ├── footer_message.png │ ├── footer_@.png │ ├── footer.png │ ├── header.png │ └── Thumbs.db ├── img │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── Thumbs.db ├── Index.html ├── js │ ├── action.js │ └── jquery-1.9.1.min.js ├── test.html └── Work.html 以上是我的文件目录数,web文件里面的内容是实验提供的, ├── include │ └── common.h ├── Makefile ├── src │ ├── common.c │ ├── epoll_serv.c │ ├── process_serv.c │ └── pthread_serv.c 这几个是我自己写的,代码如下: common.h #ifndef __COMMON_H__ #define __COMMON_H__ #define _XOPEN_SOURCE 700 // 消除sigaction相关报错 #include <stdio.h> // 标准输入输出 #include <stdlib.h> // 标准库函数 #include <string.h> // 字符串处理 #include <unistd.h> // UNIX标准函数 #include <sys/socket.h> // 套接字接口 #include <netinet/in.h> // 网络地址结构 #include <arpa/inet.h> // 地址转换函数 #include <fcntl.h> // 文件控制 #include <sys/stat.h> // 文件状态 #include <sys/sendfile.h> // 高效文件传输 #include <sys/types.h> // 系统类型 #include <dirent.h> // 目录操作 #include <errno.h> // 错误码 #include <regex.h> // 正则表达式 #include <sys/wait.h> // 关闭子进程 #include <signal.h> // 信号处理 #include <sys/epoll.h> // epoll I/O多路复用 #include <pthread.h> // 多线程头文件 #include <semaphore.h> // 信号量 #define PORT 80 // 监听端口 #define MAX_EVENTS 1024 // epoll最大事件数 #define BUFFER_SIZE 4096 // 请求缓冲区大小 #define ROOT_DIR "./web" // Web根目录,根据执行可执行文件的路径来设置 #define MAX_MATCHES 30 // 最大匹配数 /* 根据文件扩展名获取MIME类型 */ const char *get_mime_type(const char *path); /* 发送HTTP响应 */ void send_response(int fd, int status, const char *status_msg, const char *content_type, const char *body, int body_len); /* 发送文件内容 */ void send_file(int fd, const char *path); /* 处理GET请求 */ void handle_get_request(int fd, const char *path); /* 处理POST请求 */ void handle_post_request(int fd, const char *path, const char *body, int body_len); /* 处理HTTP请求 */ void handle_request(int fd, const char *request); #endif process_serv.c #include "common.h" #define WORKER_PROCESSES 1 /* 工作进程数量 */ /* 信号处理函数,用于销毁子进程 */ void read_childproc(int sig) { /* 入参检查*/ if (sig < 0) { return; } pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("Normal removed proc id: %d\n", pid); } else { printf("Abnormal removed proc id: %d\n", pid); } } } /* 进程工作 */ void worker_process(int server_fd) { /* 入参检查*/ if (server_fd <= 2) { return; } while (1) { struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen); if (client_fd < 0) { if (errno == EINTR) continue; /* 被信号中断,重试 */ perror("accept"); continue; } printf("[Worker %d] New connection: %s:%d\n", getpid(), inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); /* 读取请求 */ char buffer[BUFFER_SIZE]; ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read <= 0) { close(client_fd); continue; } buffer[bytes_read] = '\0'; fprintf(stdout, "======buffer======\n%s============\nclient_fd:%d\n", buffer, client_fd); /* 处理请求 */ handle_request(client_fd, buffer); /* 关闭连接 */ close(client_fd); } } /** * 主函数:设置服务器并创建工作进程 */ int main() { int server_fd; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); /* 创建套接字 */ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } /* 设置套接字选项(允许地址重用) */ int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt failed"); close(server_fd); exit(EXIT_FAILURE); } /* 绑定地址 */ memset(&addr, 0, addrlen); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr *)&addr, addrlen) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } /* 开始监听 */ if (listen(server_fd, SOMAXCONN) < 0) { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("Web server running on port %d...\n", PORT); printf("Root directory: %s\n", ROOT_DIR); /* 设置信号处理 */ struct sigaction act; act.sa_handler = read_childproc; sigemptyset(&act.sa_mask); act.sa_flags = SA_NOCLDSTOP | SA_RESTART; /* 注册信号 */ if (sigaction(SIGCHLD, &act, 0) < 0) { perror("sigaction failed"); close(server_fd); exit(EXIT_FAILURE); } /* 创建工作进程 */ for (int i = 0; i < WORKER_PROCESSES; i++) { pid_t pid = fork(); if (pid == 0) { /* 子进程 */ worker_process(server_fd); exit(EXIT_SUCCESS); } else if (pid < 0) { perror("fork failed"); exit(EXIT_FAILURE); } } /* 主进程等待 */ while (1) { pause(); /* 等待信号 */ } close(server_fd); return 0; } epoll_serv.c #include "common.h" /* 客户端数据结构 */ typedef struct { int fd; /* 客户端套接字描述符 */ char buffer[BUFFER_SIZE]; /* 请求数据缓冲区 */ int buffer_len; /* 当前缓冲区数据长度 */ int status; /* 客户端状态(0:等待,1:读取中,2:写入中) */ } client_data; /** * 工作进程函数(处理客户端连接) */ void worker_process(int server_fd) { /* 入参检查*/ if (server_fd <= 2) { return; } while (1) { struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addrlen); if (client_fd < 0) { if (errno == EINTR) continue; /* 被信号中断,重试 */ perror("accept"); continue; } printf("[Worker %d] New connection: %s:%d\n", getpid(), inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); /* 读取请求 */ char buffer[BUFFER_SIZE]; ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read <= 0) { close(client_fd); continue; } buffer[bytes_read] = '\0'; fprintf(stdout, "======buffer======\n%s============\n", buffer); /* 处理请求 */ handle_request(client_fd, buffer); /* 关闭连接 */ close(client_fd); } } /** * 主函数:设置服务器并运行事件循环 */ int main() { int server_fd, epoll_fd; struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); /* 1. 创建服务器套接字 */ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } /* 设置套接字选项(允许地址重用) */ int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt failed"); close(server_fd); exit(EXIT_FAILURE); } /* 2. 配置服务器地址 */ addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; /* 监听所有网络接口 */ addr.sin_port = htons(PORT); /* 监听端口 */ /* 3. 绑定套接字 */ if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } /* 4. 开始监听 */ if (listen(server_fd, SOMAXCONN) < 0) { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("Web server running on port %d...\n", PORT); printf("Root directory: %s\n", ROOT_DIR); /* 5. 创建epoll实例 */ if ((epoll_fd = epoll_create1(0)) < 0) { perror("epoll_create1 failed"); close(server_fd); exit(EXIT_FAILURE); } /* 6. 添加服务器套接字到epoll */ struct epoll_event ev; ev.events = EPOLLIN; /* 监听读事件 */ ev.data.fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) { perror("epoll_ctl failed"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } printf("Web server running on port %d...\n", PORT); printf("Root directory: %s\n", ROOT_DIR); /* 7. 分配客户端数据存储空间 */ struct epoll_event events[MAX_EVENTS]; client_data *clients = calloc(MAX_EVENTS, sizeof(client_data)); /* 8. 主事件循环 */ while (1) { /* 等待事件发生 */ int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); /* 一直等到事件发生 */ if (nfds < 0) { perror("epoll_wait"); continue; } /* 处理所有就绪事件 */ for (int i = 0; i < nfds; i++) { int fd = events[i].data.fd; /* 处理新连接 */ if (fd == server_fd) { /* 接受新连接 */ int client_fd = accept(server_fd, (struct sockaddr *)&addr, &addrlen); if (client_fd < 0) { perror("accept"); continue; } /* 设置非阻塞模式 */ fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL, 0) | O_NONBLOCK); /* 添加新客户端到epoll */ ev.events = EPOLLIN | EPOLLET; /* 边缘触发模式 */ ev.data.fd = client_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) { perror("epoll_ctl client"); close(client_fd); } /* 初始化客户端数据 */ clients[client_fd].fd = client_fd; clients[client_fd].buffer_len = 0; clients[client_fd].status = 0; printf("New connection: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } else { /* 处理现有客户端数据 */ client_data *client = &clients[fd]; /* 读取客户端数据 */ int bytes_read = read(fd, client->buffer + client->buffer_len, BUFFER_SIZE - client->buffer_len); if (bytes_read <= 0) { /* 连接关闭或错误 */ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); close(fd); memset(client, 0, sizeof(client_data)); continue; } /* 更新缓冲区 */ client->buffer_len += bytes_read; client->buffer[client->buffer_len] = '\0'; /* 检查是否收到完整HTTP请求(以空行结束) */ if (strstr(client->buffer, "\r\n\r\n") != NULL) { /* 处理请求 */ handle_request(fd, client->buffer); /* 重置缓冲区 */ client->buffer_len = 0; memset(client->buffer, 0, BUFFER_SIZE); } } } } /* 清理资源(实际不会执行到这里) */ free(clients); close(server_fd); close(epoll_fd); return 0; } pthread.c #include "common.h" #include <pthread.h> #include <errno.h> /* 线程工作函数 */ void *worker_pthread(void *arg) { /* 入参检查*/ if (NULL == arg) { return NULL; } /* 从参数中获取客户端文件描述符 */ int client_fd = *(int *)arg; free(arg); /* 释放动态分配的内存 */ printf("------------------------- work process ----------------------------\n"); char buffer[BUFFER_SIZE]; ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1); if (bytes_read < 0) { perror("read error"); close(client_fd); return NULL; } buffer[bytes_read] = '\0'; /* 确保字符串正确终止 */ /* 修复格式字符串:参数顺序应与格式匹配 */ fprintf(stdout, "======buffer======\nclient_fd:%d\t\n%s============\n", client_fd, buffer); /* ======buffer====== client_fd:5 GET /images/footer.png HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://localhost/css/style_mobile.css Connection: keep-alive ============ */ /* 处理请求 */ if (strlen(buffer) > 0) { handle_request(client_fd, buffer); } /* 关闭连接 */ close(client_fd); printf("______________________________________________________\n"); return NULL; } /** * 主函数:设置服务器并创建回收线程 */ int main() { int server_fd; struct sockaddr_in addr, client_addr; socklen_t client_len = sizeof(client_addr); /* 创建套接字 */ if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } /* 设置套接字选项(允许地址重用) */ int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt failed"); close(server_fd); exit(EXIT_FAILURE); } /* 绑定地址 */ memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } /* 开始监听 */ if (listen(server_fd, SOMAXCONN) < 0) { perror("listen failed"); close(server_fd); exit(EXIT_FAILURE); } printf("Web server running on port %d...\n", PORT); printf("Root directory: %s\n", ROOT_DIR); while (1) { /* 接受新连接 */ int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len); if (client_fd < 0) { perror("accept failed"); continue; } /* 动态分配内存传递client_fd(避免线程间竞争) */ int *client_ptr = malloc(sizeof(int)); if (!client_ptr) { perror("malloc failed"); close(client_fd); continue; } *client_ptr = client_fd; /* 创建新线程处理连接 */ pthread_t t_id; if (pthread_create(&t_id, NULL, worker_pthread, client_ptr) != 0) { perror("pthread_create failed"); free(client_ptr); close(client_fd); continue; } /* 分离线程(自动回收资源) */ pthread_detach(t_id); printf("New connection: %s:%d (thread %lu)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), (unsigned long)t_id); } close(server_fd); return 0; } 实验要求如下,帮我写一个实验报告
最新发布
08-16
void parse_request(int sockfd, HttpRequest *req) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = recv(sockfd, buffer, BUFFER_SIZE - 1, 0); if (bytes_read <= 0) { perror("recv failed"); return; } buffer[bytes_read] = '\0'; // 初始化请求结构 memset(req, 0, sizeof(HttpRequest)); // 解析请求行 char *line = strtok(buffer, "\r\n"); if (!line) return; sscanf(line, "%15s %255s %15s", req->method, req->path, req->protocol); // 解析头部 req->header_count = 0; while ((line = strtok(NULL, "\r\n")) && strlen(line) > 0) { if (req->header_count >= MAX_HEADERS) break; char *sep = strchr(line, ':'); if (!sep) continue; *sep = '\0'; // 临时分隔键值对 // 跳过值前的空格 char *value_start = sep + 1; while (*value_start == ' ') value_start++; // 分配并复制键 req->headers[req->header_count].key = strdup(line); if (!req->headers[req->header_count].key) { perror("strdup failed"); break; } // 分配并复制值 req->headers[req->header_count].value = strdup(value_start); if (!req->headers[req->header_count].value) { free(req->headers[req->header_count].key); perror("strdup failed"); break; } // 检查Content-Length头部 if (strcasecmp(req->headers[req->header_count].key, "Content-Length") == 0) { req->content_length = strtoul(req->headers[req->header_count].value, NULL, 10); } req->header_count++; } // 解析Body (POST) req->body = NULL; req->body_len = 0; if (strcmp(req->method, "POST") == 0) { char *body_start = strstr(buffer, "\r\n\r\n"); if (body_start) { body_start += 4; // 跳过空行 size_t header_len = body_start - buffer; // 检查是否已读取完整请求体 if (bytes_read - header_len >= req->content_length) { req->body_len = req->content_length; } else { req->body_len = bytes_read - header_len; } // 分配内存并复制请求体 req->body = malloc(req->body_len + 1); if (!req->body) { perror("malloc failed"); return; } memcpy(req->body, body_start, req->body_len); req->body[req->body_len] = '\0'; // 处理不完整的请求体 if (req->body_len < req->content_length) { size_t remaining = req->content_length - req->body_len; char *remaining_body = malloc(remaining + 1); if (!remaining_body) { perror("malloc failed"); return; } // 读取剩余请求体 ssize_t n = recv(sockfd, remaining_body, remaining, 0); if (n > 0) { remaining_body[n] = '\0'; // 重新分配内存并拼接请求体 char *new_body = realloc(req->body, req->body_len + n + 1); if (new_body) { req->body = new_body; memcpy(req->body + req->body_len, remaining_body, n); req->body_len += n; req->body[req->body_len] = '\0'; } } free(remaining_body); } } } } void free_request(HttpRequest *req) { for (int i = 0; i < req->header_count; i++) { free(req->headers[i].key); free(req->headers[i].value); } if (req->body) free(req->body); } const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (!ext) return "application/octet-stream"; if (strcmp(ext, ".html") == 0) return "text/html"; if (strcmp(ext, ".txt") == 0) return "text/plain"; if (strcmp(ext, ".jpg") == 0) return "image/jpeg"; if (strcmp(ext, ".png") == 0) return "image/png"; if (strcmp(ext, ".js") == 0) return "application/javascript"; if (strcmp(ext, ".css") == 0) return "text/css"; return "application/octet-stream"; } void send_response(int sockfd, int status, const char *status_msg, const char *content_type, const char *body, size_t length) { char headers[1024]; time_t now = time(NULL); struct tm *tm = gmtime(&now); char time_buf[64]; strftime(time_buf, sizeof(time_buf), "%a, %d %b %Y %H:%M:%S GMT", tm); int header_len = snprintf(headers, sizeof(headers), "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Server: SimpleWebServer\r\n" "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "Connection: close\r\n\r\n", status, status_msg, time_buf, content_type, length); send(sockfd, headers, header_len, 0); if (body && length > 0) { send(sockfd, body, length, 0); } } void handle_get(int sockfd, HttpRequest *req) { char full_path[512]; snprintf(full_path, sizeof(full_path), "%s%s", WWW_ROOT, req->path); // 安全校验:防止路径遍历攻击 if (strstr(req->path, "..")) { send_response(sockfd, 403, "Forbidden", "text/plain", "Path traversal not allowed", 26); return; } struct stat st; if (stat(full_path, &st) == -1 || S_ISDIR(st.st_mode)) { strcat(full_path, "/Index.html"); if (stat(full_path, &st) == -1) { send_response(sockfd, 404, "Not Found", "text/plain", "File not found", 13); return; } } int fd = open(full_path, O_RDONLY); if (fd == -1) { send_response(sockfd, 500, "Internal Error", "text/plain", "Cannot open file", 15); return; } const char *content_type = get_content_type(full_path); send_response(sockfd, 200, "OK", content_type, NULL, st.st_size); // 零拷贝发送文件 off_t offset = 0; while (offset < st.st_size) { ssize_t sent = sendfile(sockfd, fd, &offset, st.st_size - offset); if (sent <= 0) break; } close(fd); } /* void handle_post(int sockfd, HttpRequest *req) { debug_print("Received POST data: %.*s\n", (int)req->body_len, req->body); send_response(sockfd, 200, "OK", "text/plain", "POST received", 13); } */ void log_request(const char *client_ip, HttpRequest *req) { printf("[%s] %s %s %s\n", client_ip, req->method, req->path, req->protocol); for (int i = 0; i < req->header_count; i++) { printf(" %s: %s\n", req->headers[i].key, req->headers[i].value); } if (req->body) { printf("Body: %.*s\n", (int)req->body_len, req->body); } } void url_decode(char *dst, const char *src, size_t max_len) { size_t i = 0; while (*src && i < max_len - 1) { if (*src == '%') { // 确保有足够的后续字符 if (src[1] && src[2]) { char a = tolower(src[1]); // 统一小写处理 char b = tolower(src[2]); if (isxdigit(a) && isxdigit(b)) { // 十六进制转换 a = (a >= 'a') ? (a - 'a' + 10) : (a - '0'); b = (b >= 'a') ? (b - 'a' + 10) : (b - '0'); *dst++ = (a << 4) | b; // 位运算组合 src += 3; i++; continue; // 跳过后续处理 } } } else if (*src == '+') { *dst++ = ' '; src++; i++; continue; } // 默认情况:直接复制字符 *dst++ = *src++; i++; } *dst = '\0'; // 确保字符串终止 } void handle_post(int sockfd, HttpRequest *req) { // 1. 验证请求体 if (!req->body || req->body_len == 0) { printf("ERROR: Empty request body\n"); send_response(sockfd, 400, "Bad Request", "text/plain", "Empty request body", 18); return; } // 2. 打印原始请求体(调试用) printf("==== RAW POST DATA (%zu bytes) ====\n%.*s\n", req->body_len, (int)req->body_len, req->body); // 3. 复制并终止请求体 char *body_copy = malloc(req->body_len + 1); if (!body_copy) { send_response(sockfd, 500, "Internal Error", "text/plain", "Memory error", 12); return; } memcpy(body_copy, req->body, req->body_len); body_copy[req->body_len] = '\0'; // 4. 初始化字段缓冲区 char name[256] = {0}; char email[256] = {0}; char message[1024] = {0}; // 5. 解析键值对(添加详细日志) char *saveptr = NULL; char *pair = strtok_r(body_copy, "&", &saveptr); int field_count = 0; while (pair) { printf("Processing pair: %s\n", pair); char *eq = strchr(pair, '='); if (eq) { *eq = '\0'; // 分割键值 char *key = pair; char *value = eq + 1; // 6. 调试输出原始键值 printf(" Raw key: '%s', value: '%s'\n", key, value); // 7. 处理不同字段(大小写不敏感) if (strcasecmp(key, "name") == 0) { url_decode(name, value, sizeof(name)); printf(" Decoded name: '%s'\n", name); field_count++; } else if (strcasecmp(key, "email") == 0) { url_decode(email, value, sizeof(email)); printf(" Decoded email: '%s'\n", email); field_count++; } else if (strcasecmp(key, "message") == 0) { url_decode(message, value, sizeof(message)); printf(" Decoded message: '%s'\n", message); field_count++; } else { printf(" WARNING: Unknown field '%s'\n", key); } } else { printf(" ERROR: Invalid key-value pair (no '='): %s\n", pair); } pair = strtok_r(NULL, "&", &saveptr); } // 8. 验证字段解析 printf("\n=== PARSED FIELDS ===\n"); printf("Name: '%s'\n", name); printf("Email: '%s'\n", email); printf("Message: '%s'\n", message); printf("Fields found: %d\n", field_count); printf("=====================\n"); fflush(stdout); // 9. 发送响应 char response[2048]; int len = snprintf(response, sizeof(response), "Name: %s\nEmail: %s\nMessage: %s", name, email, message); send_response(sockfd, 200, "OK", "text/plain", response, len); // 10. 清理内存 free(body_copy);网页contact填入name、email和message,返回terminal出现error:empty request body
08-16
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <ctype.h> #include <stdbool.h> #include <unistd.h> #include <time.h> /* 定义常量 */ #define TCP_IP_MAXSIZE 2 #define MODBUS_MAX_REGISTERS 125 #define MAX_ADDR_STR_LEN 20 #define MAX_DATA_PACKET_SIZE 4096 #define COLLECTION_INTERVAL_MS 1000 /* 寄存器类型枚举 */ typedef enum { REG_V, /* V寄存器 */ REG_IX, /* IX寄存器 */ REG_QX, /* QX寄存器 */ REG_MX, /* MX寄存器 */ REG_MW, /* MW寄存器 */ REG_MD, /* MD寄存器 */ REG_DBX, /* DBX寄存器 */ REG_DBW, /* DBW寄存器 */ REG_DBD, /* DBD寄存器 */ REG_DB, /* DB寄存器 */ REG_UNKNOWN /* 未知类型 */ } RegType; /* 寄存器类型属性 */ typedef struct { RegType type; const char *prefix; /* 字符串前缀 */ bool is_bit_addressable; /* 是否支持位寻址 */ int byte_size; /* 字节大小(2或4) */ } RegTypeInfo; /* 寄存器类型信息表 */ static const RegTypeInfo reg_type_info[] = { {REG_V, "%V", false, 2}, {REG_IX, "%IX", false, 2}, {REG_QX, "%QX", false, 2}, {REG_MX, "%MX", true, 2}, /* 虽然支持位寻址,但byte_size为2 */ {REG_MW, "%MW", false, 2}, {REG_MD, "%MD", false, 4}, {REG_DBX, "%DBX", true, 2}, /* 虽然支持位寻址,但byte_size为2 */ {REG_DBW, "%DBW", false, 2}, {REG_DBD, "%DBD", false, 4}, {REG_DB, "%DB", false, 2}, {REG_UNKNOWN, "", false, 0} }; /* 设备参数列表结构体 */ typedef struct { int portfd; /* 485端口号 */ int plc_addr; /* PLC设备地址 */ char plc_ip[20]; /* TCP IP地址 */ int DeviceNumber; /* PLC编号 */ char DeviceNumber_s[16]; /* 采集设备集合标识 */ } DEVICE_ARGV_LIST; /* 采集多个设备 */ typedef struct { int Device_num; /* 采集PLC个数 */ DEVICE_ARGV_LIST DeviceArgvList[TCP_IP_MAXSIZE]; /* 参数列表集 */ } DEVICE_ARGV; /* 参数结构体 */ typedef struct { int ParaId; /* 参数ID */ char DataType; /* 参数数据类型 */ char RegAddress[MAX_ADDR_STR_LEN]; /* 参数地址 */ uint8_t RegByteValue[8];/* 采集到的寄存器值 */ } DEVICEID_PARAID; /* 设备数据结构体 */ typedef struct { char DeviceIdIndex; /* 参数个数 */ uint8_t TypeLen; /* DeviceType长度 */ uint8_t DeviceType[20]; /* 设备类型+ID */ DEVICEID_PARAID *DeviceParaID; /* 参数列表 */ } DEVICE_DATA; /* 设备编号结构体 */ typedef struct { char Number; /* 设备编号 */ char DeviceDataIndex; /* 数据库地址个数 */ DEVICE_DATA *DeviceData; /* 数据库集 */ } DEVICE_NUMBER; /* 设备参数主结构体 */ typedef struct { char date[14]; /* 时间 */ char DeviceNumberIndex; /* 设备数 */ char Poffset; /* 偏移 */ char SampleFre; /* 采集频率 */ char MessageId[10]; /* 消息ID */ DEVICE_NUMBER *DeviceNumber; /* 采集设备集 */ } DEVICE_PARA; /* 地址解析结果 */ typedef struct { RegType type; /* 寄存器类型 */ int base_addr; /* 基地址 */ int bit_offset; /* 位偏移 (-1表示无位偏移) */ } AddrParseResult; /* 地址分组结构 */ typedef struct AddrGroup { RegType type; /* 寄存器类型 */ int slave_addr; /* 从机地址 */ int portfd; /* 使用的485端口 */ int start_addr; /* 起始地址 */ int end_addr; /* 结束地址 */ int param_count; /* 包含的参数数量 */ DEVICEID_PARAID **params; /* 动态参数指针数组 */ int *bit_offsets; /* 位偏移数组 */ struct AddrGroup *next; /* 链表指针 */ } AddrGroup; /* 数据包结构 */ typedef struct { uint8_t data[MAX_DATA_PACKET_SIZE]; size_t size; uint32_t timestamp; } DataPacket; /* 全局变量 */ static AddrGroup *addr_groups_head = NULL; static DataPacket current_packet = {0}; /* 获取寄存器类型信息 */ const RegTypeInfo* get_reg_type_info(RegType type) { size_t i; for (i = 0; i < sizeof(reg_type_info)/sizeof(reg_type_info[0]); i++) { if (reg_type_info[i].type == type) { return &reg_type_info[i]; } } return &reg_type_info[sizeof(reg_type_info)/sizeof(reg_type_info[0]) - 1]; /* 返回UNKNOWN */ } /* 获取当前时间戳(毫秒) */ uint32_t get_current_timestamp() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000); } /* 解析地址(支持位偏移) */ AddrParseResult parse_address(const char *reg_addr) { AddrParseResult result = {REG_UNKNOWN, -1, -1}; char buf[MAX_ADDR_STR_LEN] = {0}; char *type_end; char *addr_part; char *dot_ptr; const RegTypeInfo* info; /* 复制地址字符串到缓冲区 */ strncpy(buf, reg_addr, MAX_ADDR_STR_LEN - 1); /* 查找寄存器类型部分 */ type_end = strchr(buf, ' '); if (!type_end) return result; /* 提取寄存器类型字符串 */ *type_end = '\0'; /* 在空格处截断 */ /* 确定寄存器类型 */ if (strcmp(buf, "%V") == 0) result.type = REG_V; else if (strcmp(buf, "%IX") == 0) result.type = REG_IX; else if (strcmp(buf, "%QX") == 0) result.type = REG_QX; else if (strcmp(buf, "%MX") == 0) result.type = REG_MX; else if (strcmp(buf, "%MW") == 0) result.type = REG_MW; else if (strcmp(buf, "%MD") == 0) result.type = REG_MD; else if (strcmp(buf, "%DBX") == 0) result.type = REG_DBX; else if (strcmp(buf, "%DBW") == 0) result.type = REG_DBW; else if (strcmp(buf, "%DBD") == 0) result.type = REG_DBD; else if (strcmp(buf, "%DB") == 0) result.type = REG_DB; else result.type = REG_UNKNOWN; /* 解析地址数值部分 */ addr_part = type_end + 1; dot_ptr = strchr(addr_part, '.'); if (dot_ptr) { /* 处理带位偏移的地址 */ *dot_ptr = '\0'; result.base_addr = atoi(addr_part); result.bit_offset = atoi(dot_ptr + 1); /* 验证是否支持位访问 */ info = get_reg_type_info(result.type); if (!info->is_bit_addressable) { result.bit_offset = -1; } } else { /* 没有位偏移 */ result.base_addr = atoi(addr_part); result.bit_offset = -1; } return result; } /* 获取从机地址 */ int get_slave_addr(DEVICE_NUMBER *dev_num) { return dev_num->Number; } /* 获取端口fd */ int get_portfd(int device_index, DEVICE_ARGV *device_argv) { int i; for (i = 0; i < device_argv->Device_num; i++) { if (device_argv->DeviceArgvList[i].DeviceNumber_s[device_index] == '1') { return device_argv->DeviceArgvList[i].portfd; } } return -1; } /* 创建新的地址分组 */ AddrGroup* create_addr_group(RegType type, int slave_addr, int portfd, int base_addr) { AddrGroup *new_group = (AddrGroup*)malloc(sizeof(AddrGroup)); if (!new_group) return NULL; new_group->type = type; new_group->slave_addr = slave_addr; new_group->portfd = portfd; new_group->start_addr = base_addr; new_group->end_addr = base_addr; new_group->param_count = 0; new_group->next = NULL; /* 初始化动态数组 */ new_group->params = (DEVICEID_PARAID**)malloc(sizeof(DEVICEID_PARAID*) * 10); new_group->bit_offsets = (int*)malloc(sizeof(int) * 10); if (!new_group->params || !new_group->bit_offsets) { free(new_group->params); free(new_group->bit_offsets); free(new_group); return NULL; } return new_group; } /* 向地址组添加参数 */ bool add_param_to_group(AddrGroup *group, DEVICEID_PARAID *param, int bit_offset) { /* 检查是否需要扩展数组 */ if (group->param_count % 10 == 0) { size_t new_size = (group->param_count + 10) * sizeof(DEVICEID_PARAID*); DEVICEID_PARAID **new_params = (DEVICEID_PARAID**)realloc( group->params, new_size); if (!new_params) return false; group->params = new_params; int *new_offsets = (int*)realloc( group->bit_offsets, (group->param_count + 10) * sizeof(int)); if (!new_offsets) return false; group->bit_offsets = new_offsets; } /* 添加参数 */ group->params[group->param_count] = param; group->bit_offsets[group->param_count] = bit_offset; group->param_count++; return true; } /* 查找匹配的地址分组 */ AddrGroup* find_matching_group(RegType type, int slave_addr, int portfd, int base_addr) { AddrGroup *current = addr_groups_head; const RegTypeInfo* info = get_reg_type_info(type); int threshold; int min_next; while (current != NULL) { if (current->type == type && current->slave_addr == slave_addr && current->portfd == portfd) { /* 检查地址连续性 (考虑阈值) */ threshold = info->byte_size / 2; /* 使用字节大小计算阈值 */ min_next = current->end_addr + threshold; if (base_addr >= current->start_addr && base_addr <= min_next) { return current; } } current = current->next; } return NULL; } /* 初始化地址分组 */ void init_addr_groups(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv) { int t1, t2, t3; AddrGroup *current; AddrGroup *next; DEVICE_NUMBER *dev_num; DEVICE_DATA *dev_data; DEVICEID_PARAID *param; AddrParseResult parsed; AddrGroup *group; int slave_addr; int portfd; /* 清空现有分组 */ current = addr_groups_head; while (current != NULL) { next = current->next; free(current->params); free(current->bit_offsets); free(current); current = next; } addr_groups_head = NULL; for (t1 = 0; t1 < device_para->DeviceNumberIndex; t1++) { dev_num = &device_para->DeviceNumber[t1]; slave_addr = get_slave_addr(dev_num); portfd = get_portfd(t1, device_argv); if (portfd == -1) continue; for (t2 = 0; t2 < dev_num->DeviceDataIndex; t2++) { dev_data = &dev_num->DeviceData[t2]; for (t3 = 0; t3 < dev_data->DeviceIdIndex; t3++) { param = &dev_data->DeviceParaID[t3]; parsed = parse_address(param->RegAddress); if (parsed.base_addr < 0 || parsed.type == REG_UNKNOWN) continue; /* 查找匹配的分组 */ group = find_matching_group( parsed.type, slave_addr, portfd, parsed.base_addr); if (group) { /* 更新地址范围 */ if (parsed.base_addr < group->start_addr) { group->start_addr = parsed.base_addr; } if (parsed.base_addr > group->end_addr) { group->end_addr = parsed.base_addr; } /* 添加参数到组 */ if (!add_param_to_group(group, param, parsed.bit_offset)) { printf("Failed to add param to group!\n"); } } else { /* 创建新分组 */ AddrGroup *new_group = create_addr_group( parsed.type, slave_addr, portfd, parsed.base_addr); if (!new_group) { printf("Failed to create new group!\n"); continue; } /* 添加到链表 */ if (!addr_groups_head) { addr_groups_head = new_group; } else { new_group->next = addr_groups_head; addr_groups_head = new_group; } /* 添加第一个参数 */ if (!add_param_to_group(new_group, param, parsed.bit_offset)) { printf("Failed to add param to new group!\n"); } } } } } } /* 从位偏移中提取位值 */ uint8_t extract_bit_value(uint8_t byte_value, int bit_offset) { if (bit_offset < 0 || bit_offset > 7) return 0; return (byte_value >> bit_offset) & 0x01; } /* 模拟MODBUS读取函数 */ int modbus_read(int portfd, int slave_addr, int start_addr, int reg_count, uint8_t *buffer) { /* 实际应用中这里应调用MODBUS库函数 */ printf("Reading MODBUS: portfd=%d, slave=%d, addr=%d, count=%d\n", portfd, slave_addr, start_addr, reg_count); int i; for (i = 0; i < reg_count * 2; i++) { buffer[i] = (uint8_t)((start_addr + i) % 256); /* 模拟数据 */ } return reg_count * 2; } /* 执行单次采集 */ void perform_single_collection() { AddrGroup *current = addr_groups_head; AddrGroup *group; const RegTypeInfo* info; int reg_count; uint8_t *read_buffer; int bytes_read; int i; DEVICEID_PARAID *param; int bit_offset; AddrParseResult parsed; int addr_offset; int buffer_offset; int bytes_to_copy; uint8_t byte_value; uint8_t bit_value; while (current != NULL) { group = current; current = current->next; info = get_reg_type_info(group->type); reg_count = group->end_addr - group->start_addr + 1; /* 根据数据类型调整寄存器计数 */ reg_count = (reg_count + info->byte_size - 1) / info->byte_size; if (reg_count > MODBUS_MAX_REGISTERS) { reg_count = MODBUS_MAX_REGISTERS; } /* 分配缓冲区 */ read_buffer = (uint8_t*)malloc(reg_count * 2); if (!read_buffer) { printf("Memory allocation failed for read buffer!\n"); continue; } /* 执行MODBUS读取 */ bytes_read = modbus_read(group->portfd, group->slave_addr, group->start_addr, reg_count, read_buffer); if (bytes_read != reg_count * 2) { printf("MODBUS read error! Expected %d bytes, got %d\n", reg_count * 2, bytes_read); free(read_buffer); continue; } /* 处理每个参数 */ for (i = 0; i < group->param_count; i++) { param = group->params[i]; bit_offset = group->bit_offsets[i]; parsed = parse_address(param->RegAddress); /* 计算数据在缓冲区中的位置 */ addr_offset = parsed.base_addr - group->start_addr; buffer_offset = addr_offset * (info->byte_size / 2); /* 2字节为单位 */ if (buffer_offset < 0 || buffer_offset >= bytes_read) { printf("Invalid buffer offset: %d\n", buffer_offset); continue; } /* 处理位访问 */ if (bit_offset >= 0) { /* 位寻址:数据长度为1字节 */ byte_value = read_buffer[buffer_offset]; bit_value = extract_bit_value(byte_value, bit_offset); memset(param->RegByteValue, 0, sizeof(param->RegByteValue)); param->RegByteValue[0] = bit_value; } else { /* 非位寻址:使用寄存器类型的byte_size */ bytes_to_copy = info->byte_size; if (buffer_offset + bytes_to_copy > bytes_read) { bytes_to_copy = bytes_read - buffer_offset; } memcpy(param->RegByteValue, read_buffer + buffer_offset, bytes_to_copy); } } free(read_buffer); } } /* 将数据添加到数据包 */ void add_to_data_packet(DEVICEID_PARAID *param) { const RegTypeInfo* info; AddrParseResult parsed; size_t required_size; uint8_t data_len; /* 解析地址获取数据类型 */ parsed = parse_address(param->RegAddress); info = get_reg_type_info(parsed.type); /* 计算所需空间 */ if (parsed.bit_offset >= 0) { /* 位寻址:数据长度1字节 */ data_len = 1; } else { /* 非位寻址:使用寄存器类型的byte_size */ data_len = (uint8_t)info->byte_size; } required_size = sizeof(int) + sizeof(uint8_t) + data_len; if (current_packet.size + required_size > MAX_DATA_PACKET_SIZE) { send_data_packet(); } /* 添加时间戳(如果新包) */ if (current_packet.size == 0) { current_packet.timestamp = get_current_timestamp(); memcpy(current_packet.data, &current_packet.timestamp, sizeof(current_packet.timestamp)); current_packet.size += sizeof(current_packet.timestamp); } /* 添加参数ID */ memcpy(current_packet.data + current_packet.size, &param->ParaId, sizeof(param->ParaId)); current_packet.size += sizeof(param->ParaId); /* 添加数据长度 */ memcpy(current_packet.data + current_packet.size, &data_len, sizeof(data_len)); current_packet.size += sizeof(data_len); /* 添加数据内容 */ memcpy(current_packet.data + current_packet.size, param->RegByteValue, data_len); current_packet.size += data_len; } /* 发送数据包 */ void send_data_packet() { size_t i; if (current_packet.size == 0) return; /* 模拟发送数据包 */ printf("\n===== Sending Data Packet =====\n"); printf("Timestamp: %u\n", current_packet.timestamp); printf("Size: %zu bytes\n", current_packet.size); printf("Content:\n"); for (i = 0; i < current_packet.size; i++) { printf("%02X ", current_packet.data[i]); if ((i+1) % 16 == 0) printf("\n"); } printf("\n===============================\n\n"); /* 重置数据包 */ current_packet.size = 0; } /* 主采集循环 */ void collection_loop(DEVICE_PARA *device_para, DEVICE_ARGV *device_argv) { uint32_t start_time; uint32_t elapsed; AddrGroup *current; int i; /* 初始化分组 */ init_addr_groups(device_para, device_argv); print_addr_groups(); /* 主循环 */ while (1) { start_time = get_current_timestamp(); /* 执行采集 */ perform_single_collection(); /* 将采集到的数据添加到数据包 */ current = addr_groups_head; while (current != NULL) { for (i = 0; i < current->param_count; i++) { add_to_data_packet(current->params[i]); } current = current->next; } /* 发送数据包 */ send_data_packet(); /* 等待下一个采集周期 */ elapsed = get_current_timestamp() - start_time; if (elapsed < COLLECTION_INTERVAL_MS) { usleep((COLLECTION_INTERVAL_MS - elapsed) * 1000); } } } /* 打印分组信息 */ void print_addr_groups() { AddrGroup *current = addr_groups_head; int group_num = 0; const RegTypeInfo* info; printf("\n===== Address Groups =====\n"); while (current != NULL) { info = get_reg_type_info(current->type); printf("Group %d: %s, slave=%d, portfd=%d, addr_range=%d-%d, params=%d\n", group_num, info->prefix, current->slave_addr, current->portfd, current->start_addr, current->end_addr, current->param_count); current = current->next; group_num++; } printf("==========================\n\n"); } /* 测试地址解析 */ void test_parse_address() { const char *test_addresses[] = { "%MX 10", "%MX 10.3", "%DBD 200", "%V 123", "%QX 55", "%UNKNOWN 30", "%DBX 100.5", "%MD 500", "%DB 300" }; int i; AddrParseResult result; const RegTypeInfo* info; printf("===== Address Parsing Test =====\n"); for (i = 0; i < sizeof(test_addresses)/sizeof(test_addresses[0]); i++) { result = parse_address(test_addresses[i]); info = get_reg_type_info(result.type); printf("Address: %s\n", test_addresses[i]); printf(" Type: %s (%d)\n", info->prefix, result.type); printf(" Base: %d\n", result.base_addr); printf(" Bit offset: %d\n", result.bit_offset); printf(" Byte size: %d\n", info->byte_size); printf(" Bit addressable: %s\n", info->is_bit_addressable ? "yes" : "no"); printf("\n"); } printf("===============================\n\n"); } int main() { /* 运行地址解析测试 */ test_parse_address(); /* 初始化设备参数和采集参数 */ DEVICE_PARA device_para = {0}; DEVICE_ARGV device_argv = {0}; int i, j, k; /* 设置示例数据 */ device_para.DeviceNumberIndex = 2; device_para.DeviceNumber = (DEVICE_NUMBER*)malloc(2 * sizeof(DEVICE_NUMBER)); memset(device_para.DeviceNumber, 0, 2 * sizeof(DEVICE_NUMBER)); /* 设备1 (包含位访问) */ device_para.DeviceNumber[0].Number = 1; device_para.DeviceNumber[0].DeviceDataIndex = 1; device_para.DeviceNumber[0].DeviceData = (DEVICE_DATA*)malloc(sizeof(DEVICE_DATA)); memset(device_para.DeviceNumber[0].DeviceData, 0, sizeof(DEVICE_DATA)); device_para.DeviceNumber[0].DeviceData->DeviceIdIndex = 3; device_para.DeviceNumber[0].DeviceData->DeviceParaID = (DEVICEID_PARAID*)malloc(3 * sizeof(DEVICEID_PARAID)); strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[0].RegAddress, "%MX 10"); device_para.DeviceNumber[0].DeviceData->DeviceParaID[0].ParaId = 101; strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].RegAddress, "%MX 10.3"); device_para.DeviceNumber[0].DeviceData->DeviceParaID[1].ParaId = 102; strcpy(device_para.DeviceNumber[0].DeviceData->DeviceParaID[2].RegAddress, "%MD 20"); device_para.DeviceNumber[0].DeviceData->DeviceParaID[2].ParaId = 103; /* 设备2 (双字节类型) */ device_para.DeviceNumber[1].Number = 2; device_para.DeviceNumber[1].DeviceDataIndex = 1; device_para.DeviceNumber[1].DeviceData = (DEVICE_DATA*)malloc(sizeof(DEVICE_DATA)); memset(device_para.DeviceNumber[1].DeviceData, 0, sizeof(DEVICE_DATA)); device_para.DeviceNumber[1].DeviceData->DeviceIdIndex = 2; device_para.DeviceNumber[1].DeviceData->DeviceParaID = (DEVICEID_PARAID*)malloc(2 * sizeof(DEVICEID_PARAID)); strcpy(device_para.DeviceNumber[1].DeviceData->DeviceParaID[0].RegAddress, "%DBD 200"); device_para.DeviceNumber[1].DeviceData->DeviceParaID[0].ParaId = 201; strcpy(device_para.DeviceNumber[1].DeviceData->DeviceParaID[1].RegAddress, "%DBX 55.2"); device_para.DeviceNumber[1].DeviceData->DeviceParaID[1].ParaId = 202; /* 设置采集参数 */ device_argv.Device_num = 2; device_argv.DeviceArgvList[0].portfd = 1; strcpy(device_argv.DeviceArgvList[0].DeviceNumber_s, "10"); device_argv.DeviceArgvList[1].portfd = 2; strcpy(device_argv.DeviceArgvList[1].DeviceNumber_s, "01"); /* 进入采集循环 */ collection_loop(&device_para, &device_argv); /* 释放资源 */ for (i = 0; i < device_para.DeviceNumberIndex; i++) { for (j = 0; j < device_para.DeviceNumber[i].DeviceDataIndex; j++) { free(device_para.DeviceNumber[i].DeviceData[j].DeviceParaID); } free(device_para.DeviceNumber[i].DeviceData); } free(device_para.DeviceNumber); return 0; } 帮我查看这份代码有没有将同一个fd,同一个从机地址同一个采集类型(指Md,DBD等)的所有采集地址RegAddress排序后只有前后地址超过设置的阈值后才分在一个组内,实现采集地址的分组高效采集
07-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值