size_t、ssize_t和socklen_t详解

本文详细介绍了C语言中size_t、ssize_t与socklen_t三种数据类型的作用和区别,包括它们在数组下标、内存管理、读写操作和套接字接口中的应用。

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

一、size_t

解释一:为了增强程序的可移植性,便有了size_t,它是为了方便系统之间的移植而定义的,不同的系统上,定义size_t可能不一样

在32位系统上 定义为 unsigned int 也就是说在32位系统上是32位无符号整形。在64位系统上定义为 unsigned long 也就是说在64位系统上是64位无符号整形。size_t一般用来表示一种计数,比如有多少东西被拷贝等。例如:sizeof操作符的结果类型是 size_t,该类型保证能容纳实现所建立的最大对象的字节大小。它的意义大致是“适于计量内存中可容纳的数据项目个数的无符号整数类型”。所以,它在数组下标和内存管理函数之类的地方广泛使用。而ssize_t这个数据类型用来表示可以被执行读写操作的数据块的大小.它和size_t类似,但必需是signed.意即:它表示的是signed size_t类型的。

typedef unsigned long size_t

二、ssize_t是signed size_t

size_t是标准C库中定义的,应为unsigned int。定义为typedef int ssize_t。而ssize_t:这个数据类型用来表示可以被执行读写操作的数据块的大小.它和size_t类似,但必需是signed.意即:它表示的是sign size_t类型的
[注] read返回的就是ssize_t, 因为返回的错误码可以为负数,所以必需用它

三、socklen_t

数据类型"socklen_t"和int应该具有相同的长度.否则就会破坏 BSD套接字层的填充.POSIX开始的时候用的是size_t, Linus Torvalds(他希望有更多的人,但显然不是很多) 努力向他们解释使用size_t是完全错误的,因为在64位结构中 size_t和int的长度是不一样的,而这个参数(也就是accept函数 的第三参数)的长度必须和int一致,因为这是BSD套接字接口标准.最终POSIX的那帮家伙找到了解决的办法,那就是创造了 一个新的类型"socklen_t".Linux Torvalds说这是由于他们 发现了自己的错误但又不好意思向大家伙儿承认,所以另外 创造了一个新的数据类型.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #include <fcntl.h> #include <pthread.h> #include <dirent.h> #include <sys/stat.h> #include <time.h> #include <ctype.h> #include <errno.h> #define PORT 8080 #define MAX_EVENTS 1024 #define THREAD_POOL_SIZE 8 #define BUFFER_SIZE 4096 #define WEB_ROOT "web" // 当前目录作为根目录 // 线程池结构 typedef struct { pthread_t *threads; int thread_count; pthread_mutex_t lock; pthread_cond_t cond; int shutdown; } ThreadPool; // 任务结构 typedef struct { int client_fd; struct sockaddr_in client_addr; } Task; // 全局任务队列 Task *task_queue; int task_count = 0; int task_capacity = 0; ThreadPool thread_pool; // HTTP响应头 const char *http_header = "HTTP/1.1 200 OK\r\n" "Server: SimpleWebServer\r\n" "Connection: close\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n\r\n"; // 十六进制字符转换 int hex_char_to_int(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return 0; } // URL解码 void url_decode(char *dst, const char *src) { char a, b; while (*src) { if (*src == '%' && (a = src[1]) && (b = src[2]) && isxdigit(a) && isxdigit(b)) { *dst++ = (hex_char_to_int(a) << 4) | hex_char_to_int(b); src += 3; } else if (*src == '+') { *dst++ = ' '; src++; } else { *dst++ = *src++; } } *dst = '\0'; } // 文件扩展名到MIME类型映射 const char *extension_to_type(const char *ext) { if (strcmp(ext, "html") == 0) return "text/html"; if (strcmp(ext, "htm") == 0) return "text/html"; if (strcmp(ext, "css") == 0) return "text/css"; if (strcmp(ext, "js") == 0) return "application/javascript"; if (strcmp(ext, "jpg") == 0 || strcmp(ext, "jpeg") == 0) return "image/jpeg"; if (strcmp(ext, "png") == 0) return "image/png"; if (strcmp(ext, "gif") == 0) return "image/gif"; if (strcmp(ext, "ico") == 0) return "image/x-icon"; if (strcmp(ext, "json") == 0) return "application/json"; if (strcmp(ext, "txt") == 0) return "text/plain"; if (strcmp(ext, "svg") == 0) return "image/svg+xml"; return "application/octet-stream"; } // 获取文件扩展名 const char *get_file_extension(const char *filename) { const char *dot = strrchr(filename, '.'); if (!dot || dot == filename) return ""; return dot + 1; } // 发送404错误响应 void send_404(int client_fd) { const char *response = "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 86\r\n\r\n" "<html><head><title>404 Not Found</title></head><body><h1>404 Not Found</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 发送400错误响应 void send_400(int client_fd) { const char *response = "HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 84\r\n\r\n" "<html><head><title>400 Bad Request</title></head><body><h1>400 Bad Request</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 发送500错误响应 void send_500(int client_fd) { const char *response = "HTTP/1.1 500 Internal Server Error\r\n" "Content-Type: text/html; charset=UTF-8\r\n" "Connection: close\r\n" "Content-Length: 92\r\n\r\n" "<html><head><title>500 Internal Error</title></head><body><h1>500 Internal Server Error</h1></body></html>"; send(client_fd, response, strlen(response), 0); } // 处理客户端请求 void handle_request(int client_fd, struct sockaddr_in client_addr) { char buffer[BUFFER_SIZE]; ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0); if (bytes_read <= 0) { close(client_fd); return; } buffer[bytes_read] = '\0'; // 解析HTTP请求 char method[16], path[256], protocol[16]; if (sscanf(buffer, "%s %s %s", method, path, protocol) != 3) { send_400(client_fd); close(client_fd); return; } // 日志输出 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("[%s] %s %s %s\n", client_ip, method, path, protocol); // URL解码 char decoded_path[256]; url_decode(decoded_path, path); printf("Decoded path: %s\n", decoded_path); // 只处理GET请求 if (strcmp(method, "GET") != 0 && strcmp(method, "POST") != 0) { send_400(client_fd); close(client_fd); return; } // 安全处理路径 char safe_path[512]; if (strcmp(decoded_path, "/") == 0) { // 尝试多个可能的默认文件 const char *default_files = {"Index.html"}; int found = 0; snprintf(safe_path, sizeof(safe_path), "%s/%s", WEB_ROOT, default_files); if (access(safe_path, F_OK) == 0) { found = 1; } if (!found) { strcpy(safe_path, WEB_ROOT "/Index.html"); } } else { // 处理路径中的斜杠问题 if (decoded_path[0] == '/') { snprintf(safe_path, sizeof(safe_path), "%s%s", WEB_ROOT, decoded_path); } else { snprintf(safe_path, sizeof(safe_path), "%s/%s", WEB_ROOT, decoded_path); } } printf("Final path: %s\n", safe_path); // 防止路径遍历攻击 if (strstr(safe_path, "..")) { printf("Path traversal attempt detected: %s\n", safe_path); send_404(client_fd); close(client_fd); return; } // 检查文件是否存在 struct stat file_stat; if (stat(safe_path, &file_stat) < 0) { perror("stat failed"); send_404(client_fd); close(client_fd); return; } // 检查是否是普通文件 if (!S_ISREG(file_stat.st_mode)) { printf("Not a regular file: %s\n", safe_path); send_404(client_fd); close(client_fd); return; } // 检查文件权限 if (access(safe_path, R_OK) != 0) { perror("access failed"); send_404(client_fd); close(client_fd); return; } // 打开文件 FILE *file = fopen(safe_path, "rb"); if (!file) { perror("fopen failed"); send_500(client_fd); close(client_fd); return; } // 获取MIME类型 const char *ext = get_file_extension(safe_path); const char *mime_type = extension_to_type(ext); // 构建HTTP响应头 char header[1024]; snprintf(header, sizeof(header), http_header, mime_type, file_stat.st_size); // 发送响应头 ssize_t sent = send(client_fd, header, strlen(header), 0); if (sent <= 0) { perror("send header failed"); fclose(file); close(client_fd); return; } // 发送文件内容 char file_buffer[BUFFER_SIZE]; size_t bytes; while ((bytes = fread(file_buffer, 1, sizeof(file_buffer), file)) > 0) { sent = send(client_fd, file_buffer, bytes, 0); if (sent <= 0) { perror("send file content failed"); break; } } fclose(file); close(client_fd); // 打印日志 printf("[%s] %s %s - Sent %ld bytes\n", client_ip, method, decoded_path, file_stat.st_size); } // 线程工作函数 void *worker_thread(void *arg) { while (1) { pthread_mutex_lock(&thread_pool.lock); // 等待任务 while (task_count == 0 && !thread_pool.shutdown) { pthread_cond_wait(&thread_pool.cond, &thread_pool.lock); } if (thread_pool.shutdown) { pthread_mutex_unlock(&thread_pool.lock); pthread_exit(NULL); } // 获取任务 Task task = task_queue[--task_count]; pthread_mutex_unlock(&thread_pool.lock); // 处理任务 handle_request(task.client_fd, task.client_addr); } return NULL; } // 初始化线程池 void thread_pool_init(ThreadPool *pool, int thread_count) { pool->threads = (pthread_t *)malloc(thread_count * sizeof(pthread_t)); if (!pool->threads) { perror("malloc failed"); exit(EXIT_FAILURE); } pool->thread_count = thread_count; pool->shutdown = 0; pthread_mutex_init(&pool->lock, NULL); pthread_cond_init(&pool->cond, NULL); // 初始化任务队列 task_capacity = 256; task_queue = (Task *)malloc(task_capacity * sizeof(Task)); if (!task_queue) { perror("malloc failed"); exit(EXIT_FAILURE); } task_count = 0; // 创建工作线程 for (int i = 0; i < thread_count; i++) { if (pthread_create(&pool->threads[i], NULL, worker_thread, NULL) != 0) { perror("pthread_create failed"); exit(EXIT_FAILURE); } } } // 添加任务到线程池 void add_task(int client_fd, struct sockaddr_in client_addr) { pthread_mutex_lock(&thread_pool.lock); // 如果任务队列已满,扩大容量 if (task_count == task_capacity) { task_capacity *= 2; Task *new_queue = (Task *)realloc(task_queue, task_capacity * sizeof(Task)); if (!new_queue) { perror("realloc failed"); pthread_mutex_unlock(&thread_pool.lock); close(client_fd); return; } task_queue = new_queue; } // 添加任务 task_queue[task_count].client_fd = client_fd; task_queue[task_count].client_addr = client_addr; task_count++; // 通知工作线程 pthread_cond_signal(&thread_pool.cond); pthread_mutex_unlock(&thread_pool.lock); } // 设置文件描述符为非阻塞 void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags == -1) { perror("fcntl F_GETFL failed"); return; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { perror("fcntl F_SETFL failed"); } } int main() { int server_fd, epoll_fd; struct sockaddr_in address; struct epoll_event event, events[MAX_EVENTS]; socklen_t addrlen = sizeof(address); // 创建服务器socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置socket选项 int opt = 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { perror("setsockopt"); close(server_fd); exit(EXIT_FAILURE); } // 绑定地址端口 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); close(server_fd); exit(EXIT_FAILURE); } // 开始监听 if (listen(server_fd, 128) < 0) { perror("listen"); close(server_fd); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); printf("Web root: %s\n", WEB_ROOT); // 创建epoll实例 epoll_fd = epoll_create1(0); if (epoll_fd < 0) { perror("epoll_create1"); close(server_fd); exit(EXIT_FAILURE); } // 添加服务器socket到epoll event.events = EPOLLIN; event.data.fd = server_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) < 0) { perror("epoll_ctl: server_fd"); close(server_fd); close(epoll_fd); exit(EXIT_FAILURE); } // 初始化线程池 thread_pool_init(&thread_pool, THREAD_POOL_SIZE); printf("Thread pool initialized with %d threads\n", THREAD_POOL_SIZE); // 主循环 while (1) { int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds < 0) { perror("epoll_wait"); break; } for (int i = 0; i < nfds; i++) { if (events[i].data.fd == server_fd) { // 处理新连接 struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len); if (client_fd < 0) { perror("accept"); continue; } // 设置非阻塞 set_nonblocking(client_fd); // 添加客户端socket到epoll event.events = EPOLLIN | EPOLLET; event.data.fd = client_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) < 0) { perror("epoll_ctl: client_fd"); close(client_fd); continue; } // 打印新连接信息 char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN); printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port)); } else { // 处理客户端请求 int client_fd = events[i].data.fd; // 获取客户端地址 struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); if (getpeername(client_fd, (struct sockaddr *)&client_addr, &addr_len) < 0) { perror("getpeername failed"); close(client_fd); continue; } // 将任务添加到线程池 add_task(client_fd, client_addr); // 从epoll中移除,避免重复处理 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, NULL); } } } // 清理 close(server_fd); close(epoll_fd); // 关闭线程池 thread_pool.shutdown = 1; pthread_cond_broadcast(&thread_pool.cond); for (int i = 0; i < thread_pool.thread_count; i++) { pthread_join(thread_pool.threads[i], NULL); } free(thread_pool.threads); free(task_queue); return 0; } 以这一版为准 运行后,windows浏览器偶尔会对GET请求报错:net:ERR_CONTENT_LENGTH_MISMATCH
最新发布
08-16
#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> #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 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 *, const char *); void not_found(int); void serve_file(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; int cgi = 0; char *query_string = NULL; // 获取请求的第一行 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'; // 只支持GETPOST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); return NULL; } // POST请求启用CGI /*if (strcasecmp(method, "POST") == 0) cgi = 1; */ // 读取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); // 处理GET请求 if (strcasecmp(method, "GET") == 0) { query_string = decoded_url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } } // 构建文件路径(使用当前目录) 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 ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)) cgi = 1; // 处理静态文件或CGI if (!cgi) serve_file(client, path); else execute_cgi(client, path, method, query_string); 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); } } // / 500 Internal Server Error / // 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, "<html><body><h1>500 Internal Server Error</h1><p>CGI execution failed.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // / 错误处理 / // void error_die(const char *sc) { perror(sc); exit(1); } // / 执行CGI脚本 / // void execute_cgi(int client, const char *path, const char *method, const char *query_string) { char buf[65536]; int cgi_output[2]; int cgi_input[2]; pid_t pid; int status; int i; 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)) numchars = get_line(client, buf, sizeof(buf)); else { 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); if (pipe(cgi_output) < 0) { cannot_execute(client); return; } if (pipe(cgi_input) < 0) { cannot_execute(client); return; } if ((pid = fork()) < 0 ) { cannot_execute(client); return; } if (pid == 0) /* 子进程: CGI脚本 */ { char meth_env[255]; char query_env[255]; char length_env[255]; dup2(cgi_output[1], 1); dup2(cgi_input[0], 0); close(cgi_output[0]); close(cgi_input[1]); 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); } execl(path, path, NULL); exit(0); } else { /* 父进程 */ 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); write(cgi_input[1], &c, 1); } 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); } } // / 读取一行 / // 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); } // / 启动服务器 / // int startup(unsigned 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); } // / 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。我现在需要写一版以IO多路复用方式实现的webserver
08-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值