(short (*)[2])malloc(size * count);

本文解析了指针与二维数组的关系,通过实例说明如何声明指向二维数组的指针及如何使用malloc为二维数组动态分配内存。

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

最近遇到这样几句代码:

short (*table)[2];


table = (short (*)[2])malloc(size * count);

顿时感觉应该和 short *table[2] 肯定不是同一类东东。经过查证,这是一个指向二维数组的指针。

int  a[4][2];
int  (*p)[2] = a;   //每行两个元素的数组,即 n 行 2列的二维数组; 
int i,j;
for(i = 0; i< 4; i++)
    for(j = 0; j<2; j++)
	printf("a[x][y] =  %d", p[x][y]);


所以不难理解之前的代码了吧~

由此可见,使用malloc 动态分配内存时,不仅仅只能是一维数组,也可以是二维的(int (*)[3]),甚至更高维数的~ 

/*! * ************************************************************************************************* * @copyright(c) 2008-2025 Shenzhen TP-LINK Technologies Co.Ltd. * * @file http.c * @brief * * @author huyushuai * @version 1.0.0 * @date 2025-08-08 * ************************************************************************************************* */ /**************************************************************************************************/ /* INCLUDE_FILES */ /**************************************************************************************************/ #include <stdio.h> #include <stdbool.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h> #include <errno.h> #include <signal.h> #include <stdatomic.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define ISspace(x) isspace((int)(x)) #define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n" #define MAX_THREADS 100 #define DOCUMENT_ROOT "htdocs" /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ typedef struct { pthread_t *threads; int count; atomic_int active_count; pthread_mutex_t mutex; pthread_cond_t cond; } ThreadPool; typedef struct { int client_sock; struct sockaddr_in client_addr; } RequestContext; ThreadPool *thread_pool = NULL; /**************************************************************************************************/ /* LOCAL_PROTOTYPES */ /**************************************************************************************************/ void *worker_thread(void *arg); void accept_request(RequestContext *ctx); void bad_request(int client); void cat(int, FILE *); void cannot_execute(int); void error_die(const char *); void execute_cgi(int, const char *, const char *, const char *); int get_line(int, char *, int); void headers(int, const char *); void not_found(int); void serve_file(int, const char *); int startup(u_short *); void unimplemented(int); void normalize_path(char *path); const char *get_content_type(const char *path); void handle_sigpipe(int sig); ThreadPool* create_thread_pool(int size); void destroy_thread_pool(ThreadPool *pool); void submit_request(ThreadPool *pool, RequestContext *ctx); /**************************************************************************************************/ /* LOCAL_FUNCTIONS */ /**************************************************************************************************/ /*! * @brief 路径规范函数 * * @param[in] path 文件路径 */ void normalize_path(char *path) { char *p = path; char *q = path; while (*p) { if (strncmp(p, "/../", 4) == 0) { p += 3; while (q > path && *--q != '/') { ; } } else if (strncmp(p, "/./", 3) == 0) { p += 2; } else if (strncmp(p, "//", 2) == 0) { p += 1; } else { *q++ = *p++; } } *q = '\0'; } /*! * @brief 忽略SIGPIPE信号处理 * * @param[in] sig SIGPIPE信号 */ void handle_sigpipe(int sig) { } /*! * @brief 创建线程池 * * @param[in] size 大小 * @return ThreadPool* */ ThreadPool* create_thread_pool(int size) { ThreadPool *pool = malloc(sizeof(ThreadPool)); if (!pool) { return NULL; } pool->threads = malloc(sizeof(pthread_t) * size); if (!pool->threads) { free(pool); return NULL; } pool->count = size; pool->active_count = 0; pthread_mutex_init(&pool->mutex, NULL); pthread_cond_init(&pool->cond, NULL); for (int i = 0; i < size; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, pool) != 0) { perror("Failed to create thread"); destroy_thread_pool(pool); return NULL; } } return pool; } // 销毁线程池 void destroy_thread_pool(ThreadPool *pool) { if (!pool) { return; } // 通知所有线程退出 for (int i = 0; i < pool->count; i++) { pthread_cancel(pool->threads[i]); } // 等待所有线程结束 for (int i = 0; i < pool->count; i++) { pthread_join(pool->threads[i], NULL); } free(pool->threads); pthread_mutex_destroy(&pool->mutex); pthread_cond_destroy(&pool->cond); free(pool); } /*! * @brief 提交请求到线程池 * * @param[in] pool 线程池 * @param[in] ctx 请求 */ void submit_request(ThreadPool *pool, RequestContext *ctx) { pthread_mutex_lock(&pool->mutex); while (pool->active_count >= pool->count) { pthread_cond_wait(&pool->cond, &pool->mutex); } atomic_fetch_add(&pool->active_count, 1); pthread_mutex_unlock(&pool->mutex); pthread_t thread; if (pthread_create(&thread, NULL, (void *(*)(void *))accept_request, ctx) != 0) { perror("Failed to create request thread"); close(ctx->client_sock); free(ctx); atomic_fetch_sub(&pool->active_count, 1); pthread_cond_signal(&pool->cond); } else { pthread_detach(thread); } } // 工作线程函数 void *worker_thread(void *arg) { ThreadPool *pool = (ThreadPool *)arg; while (1) { // 线程池管理逻辑 sleep(1); // 简化实现,实际应有任务队列 } return NULL; } /**********************************************************************/ /* A request has caused a call to accept() on the server port to * return. Process the request appropriately. * Parameters: the socket connected to the client */ /**********************************************************************/ void accept_request(RequestContext *ctx) { int client = ctx->client_sock; char buf[1024]; char method[255]; char url[255]; char path[512]; struct stat st; int cgi = 0; char *query_string = NULL; int numchars = get_line(client, buf, sizeof(buf)); size_t i = 0; size_t j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; // 只支持GET和POST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); close(client); free(ctx); return; } // POST请求启用CGI if (strcasecmp(method, "POST") == 0) { cgi = 1; } i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) { j++; } while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; // 处理GET查询参数 if (strcasecmp(method, "GET") == 0) { query_string = url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } } // 构建安全路径 if (strlen(url) == 0 || strstr(url, "..") != NULL) { bad_request(client); close(client); free(ctx); return; } snprintf(path, sizeof(path), "%s%s", DOCUMENT_ROOT, url); normalize_path(path); // 确保路径在文档根目录内 if (strncmp(path, DOCUMENT_ROOT, strlen(DOCUMENT_ROOT)) != 0) { not_found(client); close(client); free(ctx); return; } // 默认索引文件 if (path[strlen(path) - 1] == '/') { strncat(path, "index.html", sizeof(path) - strlen(path) - 1); } // 检查文件状态 if (stat(path, &st) == -1) { // 丢弃剩余头部 while ((numchars > 0) && strcmp("\n", buf) != 0) { numchars = get_line(client, buf, sizeof(buf)); } not_found(client); } else { if (S_ISDIR(st.st_mode)) { strncat(path, "/index.html", sizeof(path) - strlen(path) - 1); stat(path, &st); // 重新获取状态 } if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) { cgi = 1; } if (!cgi) { serve_file(client, path); } else { execute_cgi(client, path, method, query_string); } } /* 清理资源 */ close(client); free(ctx); /* 更新线程池状态 */ if (thread_pool) { atomic_fetch_sub(&thread_pool->active_count, 1); pthread_cond_signal(&thread_pool->cond); } } /**********************************************************************/ /* Inform the client that a request it has made has a problem. * Parameters: client socket */ /**********************************************************************/ void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "<P>Your browser sent a bad request, "); send(client, buf, sizeof(buf), 0); sprintf(buf, "such as a POST without a Content-Length.\r\n"); send(client, buf, sizeof(buf), 0); } /*! * @brief 将文件内容发送到网络客户端 * * @param[in] client 客户端套接字描述符 * @param[in] resource 文件指针(指向要发送的文件) */ void cat(int client, FILE *resource) { char buf[1024]; size_t bytes_read; while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { if (send(client, buf, bytes_read, 0) < bytes_read) { perror("send"); break; } } } /**********************************************************************/ /* Inform the client that a CGI script could not be executed. * Parameter: the client socket descriptor. */ /**********************************************************************/ void cannot_execute(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<P>Error prohibited CGI execution.\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* Print out an error message with perror() (for system errors; based * on value of errno, which indicates system call errors) and exit the * program indicating an error. */ /**********************************************************************/ void error_die(const char *sc) { perror(sc); exit(1); } /**********************************************************************/ /* Execute a CGI script. Will need to set environment variables as * appropriate. * Parameters: client socket descriptor * path to the CGI script */ /**********************************************************************/ void execute_cgi(int client, const char *path, const char *method, const char *query_string) { // 缓冲区 char buf[1024]; int cgi_output[2]; int cgi_input[2]; pid_t pid; int status; int i = 0; char c; int numchars = 1; int content_length = -1; // 默认字符 buf[0] = 'A'; buf[1] = '\0'; // 忽略大小写比较字符串 if (strcasecmp(method, "GET") == 0) { while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ { numchars = get_line(client, buf, sizeof(buf)); } } else /* POST */ { numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", buf)) { buf[15] = '\0'; if (strcasecmp(buf, "Content-Length:") == 0) { content_length = atoi(&(buf[16])); } numchars = get_line(client, buf, sizeof(buf)); } if (content_length == -1) { bad_request(client); return; } } sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); /* 建立output管道 */ if (pipe(cgi_output) < 0) { cannot_execute(client); return; } /* 建立input管道 */ if (pipe(cgi_input) < 0) { cannot_execute(client); return; } if ((pid = fork()) < 0) { cannot_execute(client); return; } if (pid == 0) /* child: CGI script */ { char meth_env[255]; char query_env[255]; char length_env[255]; // 子进程输出重定向到output管道的1端 dup2(cgi_output[1], 1); // 子进程输入重定向到input管道的0端 dup2(cgi_input[0], 0); // 关闭无用管道口 close(cgi_output[0]); close(cgi_input[1]); // CGI环境变量 sprintf(meth_env, "REQUEST_METHOD=%s", method); putenv(meth_env); if (strcasecmp(method, "GET") == 0) { sprintf(query_env, "QUERY_STRING=%s", query_string); putenv(query_env); } else { /* POST */ sprintf(length_env, "CONTENT_LENGTH=%d", content_length); putenv(length_env); } // 替换执行path execl(path, path, NULL); // int m = execl(path, path, NULL); // 如果path有问题,例如将html网页改成可执行的,但是执行后m为-1 // 退出子进程,管道被破坏,但是父进程还在往里面写东西,触发Program received signal SIGPIPE, Broken pipe. exit(0); } else { /* parent */ close(cgi_output[1]); close(cgi_input[0]); if (strcasecmp(method, "POST") == 0) { for (i = 0; i < content_length; i++) { recv(client, &c, 1, 0); ssize_t bytes_written = write(cgi_input[1], &c, 1); if (bytes_written <= 0) { perror("Failed to write to CGI"); close(cgi_input[1]); close(cgi_input[0]); break; } } } while (read(cgi_output[0], &c, 1) > 0) { send(client, &c, 1, 0); } close(cgi_output[0]); close(cgi_input[1]); waitpid(pid, &status, 0); } } /**********************************************************************/ /* Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combination. Terminates the string read * with a null character. If no newline indicator is found before the * end of the buffer, the string is terminated with a null. If any of * the above three line terminators is read, the last character of the * string will be a linefeed and the string will be terminated with a * null character. * Parameters: the socket descriptor * the buffer to save the data in * the size of the buffer * Returns: the number of bytes stored (excluding null) */ /**********************************************************************/ // 得到一行数据,只要发现c为\n,就认为是一行结束,如果读到\r,再用MSG_PEEK的方式读入一个字符,如果是\n,从socket用读出 // 如果是下个字符则不处理,将c置为\n,结束。如果读到的数据为0中断,或者小于0,也视为结束,c置为\n /*! * @brief 从socket中获取一行数据 * * @param[in] sock socket * @param[in] buf 用于保存数据的缓冲区 * @param[in] size 缓冲区的大小 * @return int 存储的字节数 */ int get_line(int sock, char *buf, int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); if ((n > 0) && (c == '\n')) { recv(sock, &c, 1, 0); } else { c = '\n'; } } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return (i); } /*! * @brief 根据文件路径的扩展名返回对应的 MIME 类型 * * @param[in] path 路径字符串 * @return const char* */ const char *get_content_type(const char *path) { const char *ext = strrchr(path, '.'); if (ext) { if (strcasecmp(ext, ".css") == 0) { return "text/css"; } if (strcasecmp(ext, ".js") == 0) { return "application/javascript"; } if (strcasecmp(ext, ".png") == 0) { return "image/png"; } if (strcasecmp(ext, ".jpg") == 0 || strcasecmp(ext, ".jpeg") == 0) { return "image/jpeg"; } if (strcasecmp(ext, ".gif") == 0) { return "image/gif"; } if (strcasecmp(ext, ".ico") == 0) { return "image/x-icon"; } if (strcasecmp(ext, ".svg") == 0) { return "image/svg+xml"; } if (strcasecmp(ext, ".woff") == 0) { return "font/woff"; } if (strcasecmp(ext, ".woff2") == 0) { return "font/woff2"; } } return "text/html"; } /*! * @brief 发送HTTP响应头 * * @param[in] client 客户端 * @param[in] filename 文件名 */ void headers(int client, const char *filename) { char buf[1024]; const char *content_type = get_content_type(filename); sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: %s\r\n", content_type); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); } /*! * @brief 404 * * @param[in] client 客户端 */ void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>The server could not fulfill\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "your request because the resource specified\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "is unavailable or nonexistent.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } /*! * @brief 如果不是CGI文件,直接读取文件返回给请求的http客户端 * * @param[in] client 客户端 * @param[in] filename 文件名 */ void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) { numchars = get_line(client, buf, sizeof(buf)); } resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); } else { headers(client, filename); cat(client, resource); } fclose(resource); } /**********************************************************************/ /* This function starts the process of listening for web connections * on a specified port. If the port is 0, then dynamically allocate a * port and modify the original port variable to reflect the actual * port. * Parameters: pointer to variable containing the port to connect on * Returns: the socket */ /**********************************************************************/ int startup(u_short *port) { int httpd = 0; struct sockaddr_in name; httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) { error_die("socket"); } memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) { error_die("bind"); } if (*port == 0) { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) { error_die("getsockname"); } *port = ntohs(name.sin_port); } if (listen(httpd, 5) < 0) { error_die("listen"); } return (httpd); } /*! * @brief 没有实现请求 * * @param[in] client 客户端 */ void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0); } int main(void) { int server_sock = -1; u_short port = 80; // 使用8080端口避免权限问题 struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); // 设置信号处理 signal(SIGPIPE, handle_sigpipe); // 创建线程池 thread_pool = create_thread_pool(MAX_THREADS); if (!thread_pool) { fprintf(stderr, "Failed to create thread pool\n"); exit(EXIT_FAILURE); } server_sock = startup(&port); printf("HTTP server running on port %d\n", port); while (1) { RequestContext *ctx = malloc(sizeof(RequestContext)); if (!ctx) { perror("malloc failed"); continue; } ctx->client_sock = accept(server_sock, (struct sockaddr *)&ctx->client_addr, &client_name_len); if (ctx->client_sock == -1) { perror("accept failed"); free(ctx); continue; } submit_request(thread_pool, ctx); } close(server_sock); destroy_thread_pool(thread_pool); return 0; } 阅读上面代码,提炼出多线程实现web_server的核心部分,(关键点在于和多进程,io多路复用的对比实现,要列出可能不同的地方)
08-09
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <cmph.h> // 三角形特征结构体 typedef struct { double angle1; double angle2; double angle3; } TriangleFeature; // 哈希表条目结构 typedef struct { int starID1; int starID2; int starID3; } HashEntry; // 生成特征键(保留8位小数精度) char* generate_feature_key(const TriangleFeature* feature) { char* key = malloc(64); snprintf(key, 64, "%.8f-%.8f-%.8f", feature->angle1, feature->angle2, feature->angle3); return key; } int main() { FILE* fp = fopen("quc_database.csv", "r"); if (!fp) { perror("无法打开数据库文件"); return 1; } // 读取并解析CSV数据 TriangleFeature* features = NULL; int* starIDs1 = NULL, *starIDs2 = NULL, *starIDs3 = NULL; char** keys = NULL; size_t count = 0, capacity = 1000; char line[256]; features = malloc(capacity * sizeof(TriangleFeature)); starIDs1 = malloc(capacity * sizeof(int)); starIDs2 = malloc(capacity * sizeof(int)); starIDs3 = malloc(capacity * sizeof(int)); keys = malloc(capacity * sizeof(char*)); while (fgets(line, sizeof(line), fp)) { if (count >= capacity) { capacity *= 2; features = realloc(features, capacity * sizeof(TriangleFeature)); starIDs1 = realloc(starIDs1, capacity * sizeof(int)); starIDs2 = realloc(starIDs2, capacity * sizeof(int)); starIDs3 = realloc(starIDs3, capacity * sizeof(int)); keys = realloc(keys, capacity * sizeof(char*)); } if (sscanf(line, "%d,%d,%d,%lf,%lf,%lf", &starIDs1[count], &starIDs2[count], &starIDs3[count], &features[count].angle1, &features[count].angle2, &features[count].angle3) == 6) { keys[count] = generate_feature_key(&features[count]); count++; } } fclose(fp); printf("成功读取 %zu 个三角形特征\n", count); // 创建CMPh适配器 cmph_io_adapter_t* source = cmph_io_vector_adapter(keys, count); // 配置CHD算法 cmph_config_t* config = cmph_config_new(source); cmph_config_set_algo(config, CMPH_CHD); // cmph_config_set_mphf_fd(config, 1); // 最小完美哈希标志 // 正确设置最小完美哈希标志 int mphf_flag = 1; // 1 表示启用最小完美哈希 cmph_config_set_mphf_fd(config, &mphf_flag); // 传递整数指针 // 构建哈希函数 cmph_t* hash = cmph_new(config); cmph_config_destroy(config); printf("最小完美哈希函数构建完成\n"); // 保存哈希函数 FILE* mph_file = fopen("triangle_features.mph", "wb"); cmph_dump(hash, mph_file); fclose(mph_file); printf("哈希函数已保存到 triangle_features.mph\n"); // 构建哈希表 HashEntry* hash_table = calloc(count, sizeof(HashEntry)); for (size_t i = 0; i < count; i++) { cmph_uint32 index = cmph_search(hash, keys[i], strlen(keys[i])); hash_table[index] = (HashEntry){ .starID1 = starIDs1[i], .starID2 = starIDs2[i], .starID3 = starIDs3[i] }; } // 保存哈希表 FILE* table_file = fopen("feature_hash_table.bin", "wb"); fwrite(&count, sizeof(size_t), 1, table_file); // 写入条目数量 fwrite(hash_table, sizeof(HashEntry), count, table_file); fclose(table_file); printf("哈希表已保存到 feature_hash_table.bin (%zu 个条目)\n", count); // 清理资源 for (size_t i = 0; i < count; i++) free(keys[i]); free(keys); free(features); free(starIDs1); free(starIDs2); free(starIDs3); free(hash_table); cmph_destroy(hash); cmph_io_vector_adapter_destroy(source); return 0; } 其中三角形特征仅需要short int 类型
07-31
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h> #include <dirent.h> #include <time.h> #include <errno.h> // 添加errno头文件 #define ISspace(x) isspace((int)(x)) #define SERVER_STRING "Server: jdbhttpd/0.2.0\r\n" // 函数声明 void* accept_request(void *); void bad_request(int); void cat(int, FILE *); void error_die(const char *); int get_line(int, char *, int); void headers(int, const char *, const char *); void not_found(int); void serve_file(int, const char *); void serve_file_post(int, const char *); int startup(unsigned short *); void unimplemented(int); void forbidden(int); void serve_directory(int, const char *); void url_decode(char *, const char *); const char *get_mime_type(const char *); void log_request(const char *, const char *, int); void set_socket_timeout(int, int); /**********************************************************************/ /* 处理客户端请求 */ /**********************************************************************/ void* accept_request(void *pclient) { int client = *(int*)pclient; free(pclient); set_socket_timeout(client, 2); char buf[65536]; int numchars; char method[255]; char url[255]; char path[1024]; // 增加路径长度 char decoded_url[1024]; // 存储解码后的URL size_t i, j; struct stat st; // 获取请求的第一行 numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; // 只支持GET和POST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST") && strcasecmp(method, "OPTIONS")) { unimplemented(client); return NULL; } // 读取URL i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) j++; while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; // URL解码 url_decode(decoded_url, url); // 记录请求日志 log_request(method, decoded_url, client); // 构建文件路径(使用当前目录) sprintf(path, "web%s", decoded_url); // 防止路径遍历攻击 if (strstr(path, "..")) { forbidden(client); close(client); return NULL; } // 处理目录请求 if (path[strlen(path) - 1] == '/') strcat(path, "Index.html"); // 检查文件/目录是否存在 if (stat(path, &st) == -1) { // 检查是否存在.html扩展名文件 char alt_path[1024]; sprintf(alt_path, "%s.html", path); if (stat(alt_path, &st) == 0) { strcpy(path, alt_path); } else { /* 跳过剩余头部 */ while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); not_found(client); close(client); return NULL; } } // 如果是目录 if ((st.st_mode & S_IFMT) == S_IFDIR) { // 检查目录中是否有index.html char index_path[1024]; sprintf(index_path, "%s/Index.html", path); if (stat(index_path, &st) == 0) { strcpy(path, index_path); } else { // 显示目录列表 serve_directory(client, path); close(client); return NULL; } } if (strcasecmp(method, "GET") == 0) { serve_file(client, path); } else if (strcasecmp(method, "POST") == 0) { serve_file_post(client, path); } close(client); return NULL; } /**********************************************************************/ /* URL解码 */ /**********************************************************************/ void url_decode(char *dest, const char *src) { char *p = dest; while (*src) { if (*src == '%') { if (src[1] && src[2]) { char hex[3] = {src[1], src[2], '\0'}; *p++ = (char)strtol(hex, NULL, 16); src += 3; } else { *p++ = *src++; } } else if (*src == '+') { *p++ = ' '; src++; } else { *p++ = *src++; } } *p = '\0'; } /**********************************************************************/ /* 获取MIME类型 */ /**********************************************************************/ const char *get_mime_type(const char *filename) { const char *dot = strrchr(filename, '.'); if (!dot) return "text/plain"; if (strcasecmp(dot, ".html") == 0 || strcasecmp(dot, ".htm") == 0) return "text/html"; if (strcasecmp(dot, ".css") == 0) return "text/css"; if (strcasecmp(dot, ".js") == 0) return "application/javascript"; if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) return "image/jpeg"; if (strcasecmp(dot, ".png") == 0) return "image/png"; if (strcasecmp(dot, ".gif") == 0) return "image/gif"; if (strcasecmp(dot, ".json") == 0) return "application/json"; if (strcasecmp(dot, ".ico") == 0) return "image/x-icon"; return "text/plain"; } /**********************************************************************/ /* 记录请求日志 */ /**********************************************************************/ void log_request(const char *method, const char *url, int client) { time_t now = time(NULL); struct tm *tm = localtime(&now); char timestamp[64]; strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); getpeername(client, (struct sockaddr*)&addr, &addr_len); char *ip = inet_ntoa(addr.sin_addr); printf("[%s] %s %s %s\n", timestamp, ip, method, url); } /**********************************************************************/ /* 处理目录列表 */ /**********************************************************************/ void serve_directory(int client, const char *path) { char buf[4096]; // 发送HTTP头 sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); // 发送HTML头部 sprintf(buf, "<html><head><title>Index of %s</title></head>", path); send(client, buf, strlen(buf), 0); sprintf(buf, "<body><h1>Index of %s</h1><ul>", path); send(client, buf, strlen(buf), 0); // 打开目录 DIR *dir = opendir(path); if (dir) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { // 跳过隐藏文件 if (ent->d_name[0] == '.') continue; char full_path[1024]; sprintf(full_path, "%s/%s", path, ent->d_name); struct stat st; stat(full_path, &st); char size_buf[32]; if (S_ISDIR(st.st_mode)) { strcpy(size_buf, "[DIR]"); } else { if (st.st_size < 1024) { sprintf(size_buf, "%ld B", st.st_size); } else if (st.st_size < 1024 * 1024) { sprintf(size_buf, "%.1f KB", st.st_size / 1024.0); } else { sprintf(size_buf, "%.1f MB", st.st_size / (1024.0 * 1024)); } } sprintf(buf, "<li><a href=\"%s\">%s</a> - %s</li>", ent->d_name, ent->d_name, size_buf); send(client, buf, strlen(buf), 0); } closedir(dir); } // 发送HTML尾部 sprintf(buf, "</ul></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 400 Bad Request */ /**********************************************************************/ void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>400 Bad Request</h1></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 403 Forbidden */ /**********************************************************************/ void forbidden(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 403 Forbidden\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>403 Forbidden</h1><p>Access to this resource is denied.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 发送文件内容 */ /**********************************************************************/ void cat(int client, FILE *resource) { char buf[65536]; fgets(buf, sizeof(buf), resource); while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); } } /**********************************************************************/ /* 错误处理 */ /**********************************************************************/ void error_die(const char *sc) { perror(sc); exit(1); } /**********************************************************************/ /* 读取一行 */ /**********************************************************************/ int get_line(int sock, char *buf, int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0); else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i); } /**********************************************************************/ /* 发送HTTP头 */ /**********************************************************************/ void headers(int client, const char *filename, const char *content_type) { char buf[1024]; (void)filename; // 未使用 strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: %s\r\n", content_type); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 404 Not Found */ /**********************************************************************/ void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>404 Not Found</h1><p>The requested URL was not found on this server.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 服务静态文件(修复二进制文件处理) */ /**********************************************************************/ void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; size_t bytes_read; long file_size; // 丢弃请求头 buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); // 使用二进制模式打开文件 resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); return; } // 获取文件大小 fseek(resource, 0, SEEK_END); file_size = ftell(resource); fseek(resource, 0, SEEK_SET); // 发送HTTP头 const char *content_type = get_mime_type(filename); // 创建并发送头部 char header_buf[2048]; sprintf(header_buf, "HTTP/1.0 200 OK\r\n"); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, SERVER_STRING); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Type: %s\r\n", content_type); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Length: %ld\r\n", file_size); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "\r\n"); send(client, header_buf, strlen(header_buf), 0); // 使用二进制模式发送文件内容 while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { ssize_t sent = send(client, buf, bytes_read, MSG_NOSIGNAL); if (sent < 0) { // 处理发送错误(如连接关闭) break; } } fclose(resource); } /**********************************************************************/ /* 服务静态文件(专为POST请求设计,包含请求体处理) */ /**********************************************************************/ void serve_file_post(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; size_t bytes_read; long file_size; size_t content_length = 0; // 用于存储请求体长度 // 1. 读取并处理请求头 while ((numchars > 0) && strcmp("\n", buf) != 0) { numchars = get_line(client, buf, sizeof(buf)); // 解析Content-Length头部 if (strncasecmp(buf, "Content-Length:", 15) == 0) { char *endptr; unsigned long temp = strtoul(buf + 15, &endptr, 10); if (endptr != buf + 15 && temp <= SIZE_MAX) { content_length = (size_t)temp; } } } // 2. 读取并丢弃请求体 if (content_length > 0) { size_t total_read = 0; struct timeval tv; tv.tv_sec = 5; // 设置5秒超时 tv.tv_usec = 0; setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (total_read < content_length) { size_t to_read = sizeof(buf); size_t remaining = content_length - total_read; if (to_read > remaining) { to_read = remaining; } bytes_read = recv(client, buf, to_read, 0); if (bytes_read <= 0) { if (bytes_read == 0) { break; // 连接关闭 } else if (errno == EAGAIN || errno == EWOULDBLOCK) { fprintf(stderr, "Request body read timeout\n"); break; // 超时 } else { perror("recv failed"); break; // 错误 } } total_read += bytes_read; } // 恢复默认超时设置 tv.tv_sec = 0; tv.tv_usec = 0; setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); } // 3. 打开并发送文件(与serve_file相同) resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); return; } // 获取文件大小 fseek(resource, 0, SEEK_END); file_size = ftell(resource); fseek(resource, 0, SEEK_SET); // 发送HTTP头 const char *content_type = get_mime_type(filename); char header_buf[2048]; sprintf(header_buf, "HTTP/1.0 200 OK\r\n"); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, SERVER_STRING); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Type: %s\r\n", content_type); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Length: %ld\r\n", file_size); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "\r\n"); send(client, header_buf, strlen(header_buf), 0); // 发送文件内容 while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { ssize_t sent = send(client, buf, bytes_read, MSG_NOSIGNAL); if (sent < 0) { // 处理发送错误 break; } } fclose(resource); } /**********************************************************************/ /* 启动服务器 */ /**********************************************************************/ int startup(unsigned short *port) { int httpd = 0; struct sockaddr_in name; /* 创建socket */ httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); /* 绑定地址 */ if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind"); /* 若预设端口为0,随机取用可用端口*/ if (*port == 0) { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); } /* 监听连接 */ if (listen(httpd, 5) < 0) error_die("listen"); return(httpd); } /**********************************************************************/ /* 501 Not Implemented */ /**********************************************************************/ void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>501 Not Implemented</h1><p>The requested method is not implemented.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // 设置套接字发送超时(在主循环accept后调用) void set_socket_timeout(int sockfd, int timeout_sec) { struct timeval tv; tv.tv_sec = timeout_sec; // 超时秒数 tv.tv_usec = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { perror("setsockopt SO_SNDTIMEO failed"); } } /**********************************************************************/ /* 主函数 */ /**********************************************************************/ int main(void) { int server_sock = -1; unsigned short port = 8080; int client_sock = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread; server_sock = startup(&port); printf("HTTP server running on port %d\n", port); signal(SIGPIPE, SIG_IGN); while (1) { /* 接受连接 */ client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len); if (client_sock == -1) { perror("accept"); continue; // 继续接受新连接,而不是退出 } // 动态分配内存传递socket描述符 int *pclient = malloc(sizeof(int)); if (!pclient) { perror("malloc failed"); close(client_sock); continue; } *pclient = client_sock; // 创建线程处理请求(不再在主线程设置超时) if (pthread_create(&newthread, NULL, accept_request, pclient) != 0) { perror("pthread_create"); free(pclient); close(client_sock); } else { // 分离线程,使其结束后自动释放资源 pthread_detach(newthread); } } close(server_sock); return(0); } 以上是我通过 多线程方式 实现的webserver. 现在我想通过I/O多路复用的方式,实现同样的功能 请给出完整的实现
最新发布
08-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值