fseek(FILE *, long, int)实例

本文介绍了如何使用fseek函数在C语言中通过指定起点和偏移量来定位文件指针,并从目标位置读取数据。通过实例演示了如何在读取文件时实现精准定位。
/************************************************* 
fseek(FILE *, long, int) 
参数分别表示指向操作文件指针,偏移量,起点
文件开头              SEEK_SET  0
文件当前指针位置      SEEK_CUR  1
文件末尾              SEEK_END  2
比如:fseek(fp,10L,SEEK_SET);
这句就是以(seek_set为起点,往后移动10个字节)=指针指向的地方 
ftell(FILE *)
得到当前指针的位置
rewind(FILE *)
把文件指针移到文件的开头 
***************************************************/
#include <stdio.h>
#include <stdlib.h>
int main()
{
    FILE *fp;
    fp=fopen("E:\\Recent Files\\test.txt","rb");
    char buffer[128];
    if (NULL==fp)
    {
       printf ("cannt open\n");
       exit (1);          
    }
    fseek(fp,-20L,SEEK_END);
    fgets(buffer,128,fp);
    printf ("%s",buffer);
    fclose(fp);
    system("pause");
    return 0;
}

#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
/* webserver.c - 支持多线程/多进程/IO复用的Web服务器 */ #include <stdio.h> #include <stdlib.h> /* 内存分配、系统命令 */ #include <string.h> #include <unistd.h> /* POSIX操作系统API */ #include <sys/socket.h> /* 套接字编程接口 */ #include <netinet/in.h> /* 互联网地址族 */ #include <arpa/inet.h> /* 互联网地址转换 */ #include <sys/stat.h> /* 文件状态信息 */ #include <fcntl.h> /* 文件控制选项 */ #include <pthread.h> /* POSIX线程 */ #include <sys/wait.h> /* 进程控制 */ #include <sys/select.h> /* IO多路复用 */ /* 服务器配置常量 */ #define PORT 80 /* 监听端口80,需要sudo运行 */ #define WEB_ROOT "./web"/* 网站根目录路径 */ #define BUFFER_SIZE 8192/* 读写缓冲区大小 */ /* 服务器运行模式 */ typedef enum { MODE_THREAD, /* 多线程模式 */ MODE_PROCESS, /* 多进程模式 */ MODE_MULTIPLEX /* IO多路复用模式 */ } ServerMode; /* HTTP状态行常量 */ const char* http_200 = "HTTP/1.1 200 OK\r\n"; const char* http_404 = "HTTP/1.1 404 Not Found\r\n"; const char* http_500 = "HTTP/1.1 500 Internal Server Error\r\n"; /* * 获取文件类型 * 参数: path - 文件路径 * 返回: 对应的MIME类型字符串 */ const char* get_mime_type(const char *path) { /* 提取文件扩展名 */ const char *ext = strrchr(path, '.'); if (!ext) { return "text/plain"; /* 默认文本类型 */ } /* 常见Web文件类型匹配 */ if (strcmp(ext, ".html") == 0) { return "text/html"; } if (strcmp(ext, ".css") == 0) { return "text/css"; } if (strcmp(ext, ".js") == 0) { return "application/javascript"; } if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) { return "image/jpeg"; } if (strcmp(ext, ".png") == 0) { return "image/png"; } return "application/octet-stream"; /* 二进制流默认类型 */ } /* * 发送文件内容到客户端 * 参数: * client_sock - 客户端套接字 * file_path - 请求的文件路径 */ void send_file(int client_sock, const char *file_path) { /* 尝试打开文件 */ FILE *file = fopen(file_path, "rb"); if (!file) { /* 文件不存在时发送404响应 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); return; } /* 获取文件大小 */ fseek(file, 0, SEEK_END); long size = ftell(file); /* 获取指针当前位置,即文件字节数 */ fseek(file, 0, SEEK_SET); /* 重置文件指针 */ /* 构造HTTP响应头 */ char header[BUFFER_SIZE]; snprintf(header, sizeof(header), "%sContent-Type: %s\r\nContent-Length: %ld\r\nConnection: close\r\n\r\n", http_200, get_mime_type(file_path), size); send(client_sock, header, strlen(header), 0); /* 分块发送文件内容 */ char buffer[BUFFER_SIZE]; size_t bytes_read; while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { send(client_sock, buffer, bytes_read, 0); } fclose(file); /* 关闭文件描述符 */ } /* * 处理HTTP请求 * 参数: client_sock - 客户端连接套接字 */ void handle_request(int client_sock) { char buffer[BUFFER_SIZE]; /* 接收客户端请求数据 */ ssize_t bytes_read = recv(client_sock, buffer, sizeof(buffer) - 1, 0); if (bytes_read <= 0) { return; } buffer[bytes_read] = '\0'; /* 确保字符串终止 */ /* 解析HTTP请求行 */ char method[16], path[256]; sscanf(buffer, "%s %s", method, path); /* 提取方法和路径 */ /* 调试输出 - 显示收到的请求 */ printf("Request: %s %s\n", method, path); /* 安全处理:防御路径遍历攻击 */ char *pos; while ((pos = strstr(path, "/../")) != NULL) /* 路径规范化,阻止通过/../回溯到上级目录 */ { memmove(pos, pos + 3, strlen(pos + 3) + 1); } /* 构造实际文件路径 */ char full_path[512]; if (strcmp(path, "/") == 0) { /* 根请求默认返回Index.html */ snprintf(full_path, sizeof(full_path), "%s/Index.html", WEB_ROOT); } else { /* 拼接web根目录和请求路径 */ snprintf(full_path, sizeof(full_path), "%s%s", WEB_ROOT, path); } /* 处理GET/POST方法 */ if (strcmp(method, "GET") == 0 || strcmp(method, "POST") == 0) { struct stat st; /* 检查文件是否存在且是普通文件 */ if (stat(full_path, &st) == 0 && S_ISREG(st.st_mode)) { send_file(client_sock, full_path); } else { /* 文件不存在时发送404 */ send(client_sock, http_404, strlen(http_404), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>404 Not Found</h1>"; send(client_sock, msg, strlen(msg), 0); } } else { /* 不支持的HTTP方法 */ send(client_sock, http_500, strlen(http_500), 0); const char *msg = "Content-Type: text/html\r\n\r\n<h1>Method Not Supported</h1>"; send(client_sock, msg, strlen(msg), 0); } close(client_sock); /* 关闭客户端连接 */ } /* * 线程处理函数(多线程模式使用) * 参数: arg - 包含客户端套接字指针 */ void* thread_handler(void *arg) { int client_sock = *((int*)arg); /* 解引用套接字 */ free(arg); /* 释放动态分配的内存 */ handle_request(client_sock); /* 处理请求 */ return NULL; } /* * 多进程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_process_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 接受客户端连接 */ int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 创建子进程处理请求 */ pid_t pid = fork(); if (pid == 0) { /* 子进程 */ close(server_sock); /* 关闭不需要的监听套接字 */ handle_request(client_sock); exit(0); /* 处理完成后退出 */ } else { /* 父进程 */ close(client_sock); /* 父进程不需要客户端套接字 */ waitpid(-1, NULL, WNOHANG); /* 非阻塞回收僵尸进程 */ } } } /* * 多线程模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_thread_mode(int server_sock) { while (1) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); /* 动态分配内存存储客户端套接字 */ int *client_sock = malloc(sizeof(int)); *client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (*client_sock < 0) continue; /* 创建线程处理请求 */ pthread_t tid; pthread_create(&tid, NULL, thread_handler, client_sock); pthread_detach(tid); /* 分离线程自动回收资源 */ } } /* * IO多路复用模式主循环 * 参数: server_sock - 服务器监听套接字 */ void run_multiplex_mode(int server_sock) { fd_set read_fds; /* 读文件描述符集合 */ int max_fd = server_sock; /* 当前最大文件描述符 */ while (1) { FD_ZERO(&read_fds); /* 清空描述符集合 */ FD_SET(server_sock, &read_fds); /* 添加服务器套接字 */ /* 等待可读事件 */ int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); if (activity < 0) break; /* 检查服务器套接字是否有新连接 */ if (FD_ISSET(server_sock, &read_fds)) { struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); int client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &addr_len); if (client_sock < 0) continue; /* 直接处理请求(单线程) */ handle_request(client_sock); } } } /* * 主函数 - 程序入口 * 参数: * argc - 命令行参数个数 * argv - 命令行参数数组 */ int main(int argc, char *argv[]) { /* 检查命令行参数 */ if (argc < 2) { fprintf(stderr, "Usage: %s <thread|process|multiplex>\n", argv[0]); return 1; } /* 解析运行模式 */ ServerMode mode; if (strcmp(argv[1], "thread") == 0) mode = MODE_THREAD; else if (strcmp(argv[1], "process") == 0) mode = MODE_PROCESS; else if (strcmp(argv[1], "multiplex") == 0) mode = MODE_MULTIPLEX; else { fprintf(stderr, "Invalid mode. Use thread, process or multiplex\n"); return 1; } /* 创建TCP套接字 */ int server_sock = socket(AF_INET, SOCK_STREAM, 0); if (server_sock < 0) { perror("Socket creation failed"); return 1; } /* 配置服务器地址 */ struct sockaddr_in server_addr = { .sin_family = AF_INET, /* IPv4地址族 */ .sin_port = htons(PORT), /* 网络字节序端口 */ .sin_addr.s_addr = INADDR_ANY /* 监听所有接口 */ }; /* 设置套接字选项 - 地址重用 */ int opt = 1; setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* 绑定套接字到地址 */ if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("Bind failed"); return 1; } /* 开始监听,最大连接队列128 */ listen(server_sock, 128); printf("Server running on port %d in %s mode\n", PORT, argv[1]); /* 根据模式运行服务器 */ switch (mode) { case MODE_PROCESS: run_process_mode(server_sock); break; case MODE_THREAD: run_thread_mode(server_sock); break; case MODE_MULTIPLEX: run_multiplex_mode(server_sock); break; } close(server_sock); /* 关闭服务器套接字 */ return 0; } 这是一份多模式的Web服务器实现代码,请在这份代码的基础上将三种模式升级为支持多个线程/进程同时并发处理的代码,并且添加进程/线程生成监控
最新发布
08-14
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值