6-字符指针_删除字符串中的空格_register_指针数组

本文介绍了字符指针的基本概念及应用,包括如何使用字符指针处理字符串中的空格,并探讨了不同类型的指针及其作用。同时,文章还讨论了const关键字在指针中的用法及void型指针的特点。
字符指针 :
  • 在给字符指针初始化赋值时, 如果赋的值为字符串常量的首元素的地址;
    则在后面的代码中, 不能通过该指针去修改字符串常量中字符的值.

练习 :

删除字符串中的空格. “hell o” === “hello”,输入的为”quit” ; 结束程序.
#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    char str[128];
    char *p = NULL;
    char *s = NULL;

    while(1)
    {
        printf("请输入一个字符串: ");
        gets(str);
        p = str;

        while(*p != '\0')
        {
            if(*p == ' ')
            {
                s = p;
                while(*s != '\0')
                {
                    *s = *(s+1);
                    s++;
                }
            }
            else
                p++;
        }
        printf("处理结果为:%s\n",str);

        if(strcmp(str, "quit") == 0)
            break;
    }

    return 0;
}
register :
  • 修饰的变量没有内存编号; 没有办法进行获取变量地址的运算(&)
指针数组 :
  • 存储类型 数据类型 * 数组名[数组的大小] ;
    指针数组 和 二级指针
指针 :
  • const int *p ; const修饰对象为*p ; 修饰p所指向的存储空间的值为只读;
  • int const *p; 与上面的功能一致, 只是形式不同.
  • int * const p; const修饰对象为变量p; p的指向不能发生改变.
const :
  • 对变量进行常量化(只读) ;
void 型指针 :
  • void *p
  • void 型指针参与运算时, 必须经过显式强制数据类型转换.
*任意类型的指针变量 在32位系统占用字节数为 : 4个字节64位下 : 8个字节 .
#### 实现Authorization头解析函数 参考格式: ```c GET/cgi-bin/checkout?a=b HTTP/1.1 Authorization: Digest username="Mufasa", realm="realm", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c0", uri="/xxxx/System/Register", response="6629fae49393a05397450978507c4ef1", algorithm=MD5, qop=auth, nc=00000001, cnonce="0a4f113b", opaque="5ccc069c403ebaf9f0171e9517f40e41" ``` **`http_parse_authorization`:** ``` LOCAL S32 http_parse_authorization(CONTEXT *context, char *value) { char *ptr = value; char *token; if (strncasecmp(ptr, "Digest ", 7) == 0) { ptr += 7; // 跳过"Digest " while ((token = strtok_r(ptr, ",", &ptr)) != NULL) { char *eq = strchr(token, '='); if (eq) { *eq = '\0'; char *key = token; char *value = eq + 1; // 去除可能的引号和空格 while (*value == ' ') value++; if (*value == '"') { value++; if (value[strlen(value)-1] == '"') value[strlen(value)-1] = '\0'; } if (strcasecmp(key, "username") == 0) { snprintf(context->digest_username, sizeof(context->digest_username), "%s", value); } else if (strcasecmp(key, "realm") == 0) { snprintf(context->digest_realm, sizeof(context->digest_realm), "%s", value); } else if (strcasecmp(key, "nonce") == 0) { snprintf(context->digest_nonce, sizeof(context->digest_nonce), "%s", value); } else if (strcasecmp(key, "uri") == 0) { snprintf(context->digest_uri, sizeof(context->digest_uri), "%s", value); } else if (strcasecmp(key, "response") == 0) { snprintf(context->digest_response, sizeof(context->digest_response), "%s", value); } else if (strcasecmp(key, "algorithm") == 0) { snprintf(context->digest_algorithm, sizeof(context->digest_algorithm), "%s", value); } else if (strcasecmp(key, "qop") == 0) { snprintf(context->digest_qop, sizeof(context->digest_qop), "%s", value); } else if (strcasecmp(key, "nc") == 0) { snprintf(context->digest_nc, sizeof(context->digest_nc), "%s", value); } else if (strcasecmp(key, "cnonce") == 0) { snprintf(context->digest_cnonce, sizeof(context->digest_cnonce), "%s", value); } else if (strcasecmp(key, "opaque") == 0) { snprintf(context->digest_opaque, sizeof(context->digest_opaque), "%s", value); } } } } return OK; } ``` ——我这个解析函数可不可行,详细地一行一行地分析下该函数是怎么读入参数的
09-10
该模块目前仅支持WS-UsernameToken认证,现在我要新增digest认证,目前已做的修改如下(onvif_proc_data_srv函数只修改了一半),请你补全剩下的修改(包括onvif_proc_data_srv函数以及其他需要修改的地方) #### 流程梳理 - 初始化认证状态:`digest_authenticated`=0,`wss_authenticated`=0。 - 检查Digest凭据: - 如果有,验证,成功则`digest_authenticated`=1;失败则返回401。 - 如果没有,继续。 - 解析SOAP消息(如果前面返回了,就无需解析)。 - 在解析SOAP之后,检查SOAP头中是否有`WSS`凭据: - 如果有,进行`WSS`验证(调用原有的`soap_usernametoken_auth`等函数): 成功:`wss_authenticated`=1 失败:构造`SOAP Fault`,设置上下文返回类型为`SOAP_FAULT`,然后返回错误。 - 如果没有,`wss_authenticated`保持0。 - 检查是否至少有一个认证通过:if (`digest_authenticated` || `wss_authenticated`) - 如果是,则继续后续处理(调用`soap_serve_request`)。 - 否则,返回401(带`WWW-Authenticate`头)。 **认证组合测试**: | 测试用例 | 预期结果 | | ------------------------ | -------------- | | 有效`Digest` + 无`WSS` | 200 OK | | 有效`Digest` + 有`效WSS` | 200 OK | | 有效`Digest` + 无效`WSS` | 400/SOAP Fault | | 无效`Digest` + 有效`WSS` | 401 | | 无效`Digest` + 无效`WSS` | 401 | | 无`Digest` + 有效`WSS` | 200 OK | | 无`Digest` + 无效`WSS` | 400/SOAP Fault | | 无`Digest` + 无`WSS` | 401 | #### 修改步骤 - 修改CONTEXT结构体,增加一些字段来存储Digest认证相关的信息。 - 对`http_parse.c`中解析HTTP头部的函数,增加对Authorization头的解析 - 实现Digest认证验证函数 - 生成nonce和管理nonce,比如设置nonce的过期时间,防止重放攻击 - 修改`onvif_proc_data_srv`函数(`onvif_srv.c`),在解析SOAP之前,先检查是否有Digest认证头并进行验证。 - 在401响应的HTTP报头添加`WWW-Authenticate`头。 #### HTTP解析流程 在`http_parser.c`中,HTTP报文的解析主要由`http_parser()`函数完成。该函数通过以下步骤将报文内容读入CONTEXT结构体: ##### 1. 初始化CONTEXT结构体 - 分配头部缓冲区(`context->head_buf`),大小为`HTTP_HEAD_BUF_SIZE`。 - 初始化CONTEXT中的各个字段,包括`head_end`(指向缓冲区当前写入位置)、`hbuf_data_end`(指向已接收数据的末尾)等。 - 设置套接字为非阻塞模式。 ##### 2. 解析HTTP头部 - 调用`http_parse_header(context)`函数解析HTTP头部。 - 读取请求行(第一行):调用`http_read_line`读取一行,然后通过`http_parse_method`解析HTTP方法(GET/POST等),`http_parse_url`解析URL(包括路径和查询参数),`http_parse_version`解析HTTP版本。 - 解析请求头字段:循环调用`http_read_line`读取每一行,直到遇到空行(表示头部结束)。每行按`key: value`格式解析,通过`http_parse_key`函数处理。该函数会调用注册的关键字解析器(如`http_parse_host`、`http_parse_content_type`等)将对应的值存入CONTEXT的字段中。 - 例如,Host字段的值会存入`context->host`。 ##### 3. 解析HTTP内容(Body) - 如果有内容(如POST请求),根据`Content-Length`或`Transfer-Encoding`处理: - 对于普通POST请求,调用`http_read_content`函数读取内容到`context->content_buf`。 - 对于文件上传(`multipart/form-data`),会处理`boundary`,并调整内容指针- 读取的内容会被存入`context->content_buf`,长度由`context->content_len`记录。 ##### 4. 后续处理 - 根据解析出的URL路径,确定服务组(如`ONVIF`或`HTTPD`),并调用对应的`data_srv`函数进行处理。 - 处理结果会被放入响应缓冲区,通过`http_make_response`或`http_make_file_response`发送。 #### **步骤一:修改CONTEXT结构体(httpd.h)** 在CONTEXT结构体中增加Digest认证相关的字段: ```c #define LEN_USERNAME 256 #define LEN_REALM 256 #define LEN_DIGEST_NONCE 256 #define LEN_URI 256 #define LEN_RESPONSE 256 #define LEN_ALGORITHM 64 #define LEN_QOP 64 #define LEN_NC 32 #define LEN_CNONCE 64 #define LEN_OPAQUE 256 typedef struct _CONTEXT { /* 现有字段... */ /* 新增Digest认证相关字段 */ BOOL digest_authenticated; /* Digest认证是否通过 */ BOOL wss_authenticated; /* Digest认证是否通过 */ char digest_username[LEN_USERNAME]; /* Digest认证用户名 */ char digest_realm[LEN_REALM]; /* Digest认证域 */ char digest_nonce[LEN_DIGEST_NONCE]; /* 服务器生成的nonce */ char digest_uri[LEN_URI]; /* 请求的URI */ char digest_response[LEN_RESPONSE]; /* 客户端计算的响应值 */ char digest_algorithm[LEN_ALGORITHM];/* 算法类型 */ char digest_qop[LEN_QOP]; /* 保护质量 */ char digest_nc[LEN_NC]; /* 随机数计数器 */ char digest_cnonce[LEN_CNONCE]; /* 客户端随机数 */ char digest_opaque[LEN_OPAQUE]; /* 服务器不透明数据 */ /* 其他现有字段... */ } CONTEXT; ``` #### 步骤二:解析Authorization头(`http_parser.c`) **添加Authorization头解析器** 在`http_parser_init()`函数中添加: ```c void http_parser_init() { /* 现有初始化代码... */ /* 添加Authorization头解析器 */ http_key_parser_add("Authorization", http_parse_authorization); /* ... */ } ``` **实现Authorization头解析函数** 参考`http`头格式: ```c GET/cgi-bin/checkout?a=b HTTP/1.1 Authorization: Digest username="Mufasa", realm="realm", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c0", uri="/xxxx/System/Register", response="6629fae49393a05397450978507c4ef1", algorithm=MD5, qop=auth, nc=00000001, cnonce="0a4f113b", /* opaque="5ccc069c403ebaf9f0171e9517f40e41" */ ``` 辅助函数:`trim_whitespace` - 去除键前后的空格,避免因空格导致匹配失败。 - 十六进制字符转数值 ```c /* 移除字符串两端的空格 */ void trim_whitespace(char *str) { char *end; /* 去除前导空格 */ while (isspace((unsigned char)*str)) str++; /* 全空格字符串处理 */ if (*str == 0) return; /* 去除尾部空格 */ end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; *(end + 1) = '\0'; } /* 十六进制字符转数值 */ int hex_val(char c) { return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10; } ``` **`http_parse_authorization`:** 该函数假设各参数之间使用逗号分隔,且参数值中不包含逗号。 ```c LOCAL S32 http_parse_authorization(CONTEXT *context, char *value) { char *parse_ptr = value; char *token; int error_count = 0; /* 检查Digest认证类型 */ if (strncasecmp(parse_ptr, "Digest ", 7) != 0) { return OK; } parse_ptr += 7; /* 跳过前缀 */ char *saveptr; /* strtok_r上下文指针 */ token = strtok_r(parse_ptr, ",", &saveptr); while (token != NULL) { /* 分割键值对 */ char *eq = strchr(token, '='); if (!eq) { HTTPD_DEBUG("Invalid param format: %s", token); error_count++; token = strtok_r(NULL, ",", &saveptr); continue; } /* 分割键值 */ *eq = '\0'; char *key = token; char *val = eq + 1; /* 移除键值两端的空格 */ trim_whitespace(key); trim_whitespace(val); /* 处理引号包裹的值 */ size_t vlen = strlen(val); if (vlen >= 2 && val[0] == '"' && val[vlen-1] == '"') { val[vlen-1] = '\0'; /* 移除尾部引号 */ memmove(val, val+1, vlen-1); /* 移除头部引号 */ vlen -= 2; /* 更新长度 */ } /* 存储到CONTEXT字段 */ if (strcasecmp(key, "username") == 0) { strncpy(context->digest_username, val, LEN_USERNAME-1); } else if (strcasecmp(key, "realm") == 0) { strncpy(context->digest_realm, val, LEN_REALM-1); } else if (strcasecmp(key, "nonce") == 0) { strncpy(context->digest_nonce, val, LEN_DIGEST_NONCE-1); } else if (strcasecmp(key, "uri") == 0) { /* 规范化URI路径(移除查询参数)*/ char *query_start = strchr(val, '?'); if (query_start) *query_start = '\0'; strncpy(context->digest_uri, val, LEN_URI-1); } else if (strcasecmp(key, "response") == 0) { strncpy(context->digest_response, val, LEN_RESPONSE-1); } else if (strcasecmp(key, "algorithm") == 0) { strncpy(context->digest_algorithm, val, LEN_ALGORITHM-1); } else if (strcasecmp(key, "qop") == 0) { strncpy(context->digest_qop, val, LEN_QOP-1); } else if (strcasecmp(key, "nc") == 0) { strncpy(context->digest_nc, val, LEN_NC-1); } else if (strcasecmp(key, "cnonce") == 0) { strncpy(context->digest_cnonce, val, LEN_CNONCE-1); } else if (strcasecmp(key, "opaque") == 0) { strncpy(context->digest_opaque, val, LEN_OPAQUE-1); } else { HTTPD_DEBUG("Unknown Digest param: %s=%s", key, val); } token = strtok_r(NULL, ",", &saveptr); } /* 检查必需参数是否齐全 */ if (strlen(context->digest_response) == 0) { HTTPD_WARNING("Missing required response field"); return ERROR; } return (error_count > 3) ? ERROR : OK; } ``` #### 步骤三:生成响应Authorization头 `onvif_srv.c`文件,`onvif_make_response(CONTEXT *context)`函数会在状态码非200时调用`http_make_response()`函数发送HTTP错误响应。 ```c #define HTTP_UNAUTHORIZED 401 LOCAL S32 onvif_make_response(CONTEXT *context) { /* 其他字段... */ if (context->code == HTTP_REQ_OK) { if (soap->use_udp == FALSE && soap->xml_buf.start != NULL && soap->xml_buf.start != soap->xml_buf.last) { ONVIF_TRACE("Onvif_make_response return onvif_send_http_rsp_packet."); ret = onvif_send_http_rsp_packet(soap); goto out; } else { ONVIF_TRACE("Onvif_make_response return OK."); ret = OK; goto out; } } ONVIF_TRACE("Onvif_make_response return http_make_response."); ret = http_make_response(context); out: /* 清除content内容 */ http_reset_context(context); return ret; } ``` 在`http_make_response()`函数中,通过`http_send_rsp_header()`设置HTTP响应报文头部。因此,需要修改`http_send_rsp_header()`函数,使其能够在HTTP状态码为401时添加`WWW-Authenticate`头部。 ```c /* Location */ if (HTTP_MOVE_TEMPORARILY == context->code) { // ...原有Location代码... } /* ========== 新增Digest认证头 ========== */ if (HTTP_UNAUTHORIZED == context->code) { /* 必需字段: realm, nonce, qop */ context->digest_realm = “Restricted Area”; /* 生成带时效的nonce */ if (generate_digest_nonce(context) != OK) { return ERROR; } length = snprintf(context->head_end, (context->head_buf_end - context->head_end), "WWW-Authenticate: Digest realm=\"Restricted Area\", nonce=\"%s\", qop=\"auth\"", context->digest_nonce); /* 结束头部 */ length += snprintf(context->head_end + length, (context->head_buf_end - context->head_end - length), "\r\n"); context->head_end += length; context->header_len += length; } /* Access-Control-Allow-Origin */ if (context->origin == TRUE) { /* ...原有代码... */ } ``` #### 步骤四:nonce生成及过期管理: ```c #include <openssl/rand.h> #include <openssl/err.h> #include <time.h> #include <string.h> #define ERROR_CRYPTO_FAIL -1 #define ERROR_BUFFER_OVERFLOW -2 #define OK 0 S32 generate_digest_nonce(CONTEXT *context) { /* 数据源:时间戳+随机数 */ struct { time_t timestamp; /* 精确到秒的时间戳 */ time_t expiration_time; /* 过期时间 */ unsigned char rand_bytes[16]; /* 强随机数 */ } nonce_seed; /* 取当前时间戳 */ nonce_seed.timestamp = time(NULL); nonce_seed.expiration_time = nonce_seed.timestamp + NONCE_LIFETIME; /* 过期时间 */ /* 生成密码学强随机数 (使用OpenSSL RAND_bytes) */ if (RAND_bytes(nonce_seed.rand_bytes, sizeof(nonce_seed.rand_bytes)) != 1) { return ERROR_CRYPTO_FAIL; /* 随机数生成失败 */ } /* 转为十六进制字符串格式 */ char hex_buffer[2 * sizeof(nonce_seed) + 1]; const unsigned char *ptr = (const unsigned char *)&nonce_seed; for (size_t i = 0; i < sizeof(nonce_seed); i++) { snprintf(hex_buffer + 2*i, 3, "%02x", ptr[i]); } hex_buffer[2 * sizeof(nonce_seed)] = '\0'; /* 组合realm和hex_buffer生成nonce:realm:hex_buffer" */ int len = snprintf(context->digest_nonce, LEN_DIGEST_NONCE, "%s:%s", context->digest_realm, hex_buffer); if (len < 0 || len >= LEN_DIGEST_NONCE) { return ERROR_BUFFER_OVERFLOW; } return OK; } /* NONCE验证函数(应在认证流程中调用) */ int validate_nonce(const char *nonce_str) { /* 提取时间结构体部分(跳过realm前缀) */ const char *hex_seed = strchr(nonce_str, ':') + 1; size_t seed_len = strlen(hex_seed)/2; /* 将HEX转回二进制结构 */ struct { time_t timestamp; time_t expiration_time; unsigned char rand_bytes[16]; } seed; for (size_t i = 0; i < seed_len; i++) { sscanf(hex_seed + 2*i, "%2hhx", (unsigned char*)&seed + i); } /* 过期判断 */ time_t current_time = time(NULL); if (current_time > seed.expiration_time) { return 0; /* 过期 */ } return 1; /* 有效 */ } ``` #### 步骤五:修改`onvif_proc_data_srv`函数(`onvif_srv.c`)(待修改) 在`onvif_proc_data_srv`函数中,在解析SOAP之前,先检查是否有Digest认证头,并进行验证。 ```c S32 onvif_proc_data_srv(CONTEXT *context) { /* 现有代码... */ /* 在解析SOAP之前添加Digest认证检查 */ context->digest_authenticated = FALSE; /* 检查是否有Digest认证头 */ if (context->digest_username[0] != '\0') { /* 执行Digest认证 */ if (check_digest_auth(context) == OK) { context->digest_authenticated = TRUE; ONVIF_TRACE("Digest authentication successful"); } else { ONVIF_TRACE("Digest authentication failed"); /* 认证失败,返回401 */ context->code = HTTP_UNAUTHORIZED; return ERROR; } } /* 检查是否至少有一个认证通过 */ if (!context->digest_authenticated && !soap->wss_authenticated) { /* 没有提供任何认证凭据,返回401 */ context->code = HTTP_UNAUTHORIZED; return ERROR; } /* 继续原有处理逻辑... */ ``` #### 步骤六:实现Digest认证验证函数 实现一个函数,用于验证Digest认证的响应是否正确。 ```c S32 check_digest_auth(CONTEXT *context) { if (!context) return -1; /* 验证必要参数存在 */ if (strlen(context->digest_username) == 0 || strlen(context->digest_realm) == 0 || strlen(context->digest_nonce) == 0 || strlen(context->digest_uri) == 0 || strlen(context->digest_response) == 0) { ONVIF_WARN("Missing required Digest auth parameters"); return -1; } /* 获取用户对应的密码 */ char stored_pwd[USER_MANAGEMENT_PASSWD_MAX_STR_LEN + 1] = {0}; if (strcmp(context->digest_username, TP_ROOT_USER) == 0) { /* 管理员账户 */ char *plain_pwd = tpssl_rsa_decrypt(PRI_KEY_FILE_PATH, (U8 *)TP_DEFAULT_PSWD, (U32)strlen(TP_DEFAULT_PSWD)); if (!plain_pwd) return -1; strncpy(stored_pwd, plain_pwd, sizeof(stored_pwd)-1); ONVIF_FREE(plain_pwd); } else { /* 第三方账户 */ THIRD_ACCOUNT_USER_MANAGEMENT third_acct; if (0 == ds_read(THIRD_ACCOUNT_PATH, &third_acct, sizeof(third_acct))) { char *plain_pwd = tpssl_rsa_decrypt(PRI_KEY_FILE_PATH, (U8 *)third_acct.ciphertext, (U32)strlen(third_acct.ciphertext)); if (!plain_pwd) return -1; strncpy(stored_pwd, plain_pwd, sizeof(stored_pwd)-1); ONVIF_FREE(plain_pwd); } else { return -1; } } /* 计算HA1 = MD5(username:realm:password) */ char ha1_input[256]; snprintf(ha1_input, sizeof(ha1_input), "%s:%s:%s", context->digest_username, context->digest_realm, stored_pwd); char ha1[33]; tpssl_md5_hex(ha1_input, ha1); memset(stored_pwd, 0, sizeof(stored_pwd)); /* 立即清除密码 */ /* 计算HA2 = MD5(method:uri) */ char ha2_input[256]; snprintf(ha2_input, sizeof(ha2_input), "%s:%s", http_method_str(context->method), context->digest_uri); char ha2[33]; tpssl_md5_hex(ha2_input, ha2); /* 计算预期响应 = MD5(HA1:nonce:nc:cnonce:qop:HA2) */ char resp_input[512]; snprintf(resp_input, sizeof(resp_input), "%s:%s:%s:%s:%s:%s", ha1, context->digest_nonce, context->digest_nc, context->digest_cnonce, context->digest_qop, ha2); char expected_resp[33]; tpssl_md5_hex(resp_input, expected_resp); /* 验证响应值 */ if (strcasecmp(expected_resp, context->digest_response) == 0) { ONVIF_TRACE("Digest auth success for user: %s", context->digest_username); return 0; /* 认证成功 */ } ONVIF_WARN("Digest auth failed. Expected: %s, Received: %s", expected_resp, context->digest_response); return -1; /* 认证失败 */ } #include <openssl/md5.h> /* 计算字符串的MD5哈希值(十六进制格式) */ void tpssl_md5_hex(const char *input, char *output) { if (!input || !output) return; unsigned char digest[MD5_DIGEST_LENGTH]; MD5_CTX context; MD5_Init(&context); MD5_Update(&context, input, strlen(input)); MD5_Final(digest, &context); for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { sprintf(output + (i * 2), "%02x", digest[i]); } output[32] = '\0'; /* MD5总是32字符 */ } /* 将HTTP方法枚举转换为字符串 */ const char *http_method_str(HTTP_METHOD method) { switch(method) { case HTTP_GET: return "GET"; case HTTP_POST: return "POST"; case HTTP_PUT: return "PUT"; case HTTP_DELETE: return "DELETE"; default: return "UNKNOWN"; } } ```
最新发布
09-13
#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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值