📄 web_server.c
字号:
/* web_server.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#include <sys/stat.h>#include <string.h>#define MAX_QUE_CONN_NM 5int start_server(int portnum);int ignore_others(FILE *fp);int process_request(char *rq, int fd);int resp_cannot_do(int fd);int resp_not_exist(char *fn);int resp_do_404(char *item, int fd);char *get_filename_extension(char *fn); int is_a_dir(char *fn);int is_a_cgi_file(char *fn);int resp_do_ls(char *dir, int fd);int resp_do_exec(char *prog, int fd);int resp_do_cat(char *fn, int fd);int main(int argc, char *argv[]){ int sock, fd; FILE *fpin; char request[BUFSIZ]; if(argc == 1) { fprintf(stderr, "usage: ./web_server portnum\n"); exit(1); } if ((sock = start_server(atoi(argv[1]))) < 0) { perror("start_server"); exit(1); } while(1) { fd = accept(sock, NULL, NULL); /* 接收请求 */ fpin = fdopen(fd, "r"); fgets(request, BUFSIZ, fpin); /* 读取客户端的请求 */ ignore_others(fpin); /* 跳过其他命令 */ process_request(request, fd); /* 接收客户端的请求 */ fclose(fpin); } exit(0);}/* 启动服务器 */int start_server(int portnum){ struct sockaddr_in server_sockaddr, client_sockaddr; int sin_size, recvbytes; int sockfd, client_fd; /*建立socket连接*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); return -1; } int i = 1;/* 使得重复使用本地地址与套接字进行绑定 */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /*设置sockaddr_in 结构体中相关参数*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(portnum); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero), 8); /* 调用绑定函数 */ if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) == -1) { perror("bind"); return -1; } if (listen(sockfd, MAX_QUE_CONN_NM) == -1) /* 调用listen函数*/ { perror("listen"); return -1; } return sockfd;}/* 跳过其他命令 */int ignore_others(FILE *fp){ char buf[BUFSIZ]; while(fgets(buf, BUFSIZ, fp) != NULL && strcmp(buf,"\r\n") != 0); return 0;}/* 处理请求包 */int process_request(char *rq, int fd){ char cmd[BUFSIZ], arg[BUFSIZ]; if (fork() != 0) { return 1; /*父进程立即返回,子进程继续往下执行*/ } strcpy(arg, "./"); if(sscanf (rq, "%s %s", cmd, arg + 2) != 2) /* 解析请求行 */ { return 1; } if (strcmp(cmd, "GET") != 0) /* 只处理GET方法 */ { resp_cannot_do(fd); } else if (not_exist(arg)) /* 若请求的资源不存在则返回失败信息 */ { resp_do_404(arg,fd); } else if (is_a_dir(arg)) /* 若请求的资源是一个目录则列出目录内容 */ { resp_do_ls(arg, fd); } else if (is_a_cgi_file(arg)) /* 若请求的资源是一个.cgi文件则运行它 */ { resp_do_exec(arg, fd); } else /* 若是其他文件,则表示它,文本/图像/声音 */ { resp_do_cat(arg, fd); } return 0;}/* 打报头 */int pack_header(FILE *fp, char *content_type){ fprintf(fp, "HTTP/1.0 200 ok\r\n"); if (content_type) { fprintf(fp, "Content-type:%s\r\n", content_type); return 0; } return 1;}/* 未定义P命令的处理方法 */int resp_cannot_do(int fd){ FILE *fp = fdopen(fd, "w"); fprintf(fp, "HTTP/1.1 501 Not Implemented\r\n"); fprintf(fp, "Content-type: text/plain\r\n"); fprintf(fp, "\r\n"); fprintf(fp, "That command is not yet implemented\r\n"); fclose(fp); return 0;}/* 判断资源是否存在 */int not_exist(char *fn){ struct stat info; return (stat(fn, &info) == -1);}/* 用户请求的资源不存在时处理方法 */int resp_do_404(char *item, int fd){ FILE *fp = fdopen(fd, "w"); fprintf(fp, "HTTP/1.1 404 Not Found\r\n"); fprintf(fp, "Content-type: text/plain\r\n"); fprintf(fp, "\r\n"); fprintf(fp, "The item you requested: %s\r\nis not found\r\n", item); fclose(fp); return 0;}char *get_filename_extension(char *fn) { char *cp; if ((cp = strrchr(fn, '.')) != NULL) { return (cp + 1); } return NULL;}/* 判断用户请求的资源是否一个目录 */int is_a_dir(char *fn){ struct stat info; return (stat(fn, &info) != -1 && S_ISDIR(info.st_mode));}/* 判断用户请求的资源是否一个cgi文件 */int is_a_cgi_file(char *fn){ return (strcmp(get_filename_extension(fn), "cgi") == 0);}/* 处理列出目录信息的请求 */int resp_do_ls(char *dir, int fd){ FILE *fp = fdopen(fd, "w"); /* 打开套接字 */ pack_header(fp, "text/plain"); /*向客户端发送状态行和实体头信息*/ fprintf(fp, "\r\n"); fflush(fp); dup2(fd, 1); /*把套接字绑定到标准输出*/ dup2(fd, 2); /*把套接字绑定到标准出错*/ close(fd); execlp("ls", "ls", "-l", dir, NULL); /*执行“ls -l”*/ perror(dir); return 0;}/* 处理执行一个可执行程序的请求 */int resp_do_exec(char *prog, int fd){ FILE *fp = fdopen(fd, "w"); /* 打开套接字 */ pack_header(fp, NULL); /*向客户端发送状态行和实体头信息*/ fflush(fp); dup2(fd,1); /*把套接字绑定到标准输出*/ dup2(fd,2); /*把套接字绑定到标准出错*/ close(fd); execl(prog, prog, NULL); perror(prog); return 0;}/* 处理显示一个文件的请求 */int resp_do_cat(char *fn, int fd){ char *extension = get_filename_extension(fn); /* 获得文件扩展名 */ char *content = "text/plain"; /* 默认为显示文本文件 */ FILE *fpsock, *fpfile; int c; /* 根据文件的扩展名判断文件的类型 */ if (strcmp(extension, "html") == 0) { content = "text/html"; /* 显示网页 */ } else if (strcmp(extension, "gif") == 0) { content = "image/gif"; /* 显示gif文件 */ } else if (strcmp(extension, "jpg") == 0) { content = "image/jpg"; /* 显示jpg文件 */ } else if (strcmp(extension, "jpeg") == 0) { content = "image/jpeg"; /* 显示jpeg文件 */ } fpsock = fdopen(fd, "w"); fpfile = fopen(fn, "r"); if (fpsock != NULL && fpfile != NULL) { pack_header(fpsock, content); fprintf(fpsock, "\r\n"); while ((c = getc(fpfile)) != EOF) { putc(c, fpsock); } fclose(fpfile); fclose(fpsock); return 0; } return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -