# HTTP Server **Repository Path**: linkvi/HTTP-Server ## Basic Information - **Project Name**: HTTP Server - **Description**: The multi-process server of this version use signal function avoid zombie process. - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2017-11-13 - **Last Updated**: 2023-04-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # HTTP Server The multi-process server of this version use signal function avoid zombie process. 1、is_http_protocol函数 函数原型:int is_http_protocol(char *msg_from_client) 函数作用:判断收到的请求报文是否是http协议的请求。 msg_from_client:从已连接套接口读到的数据。 函数逻辑: (1)读取服务器端收到的数据第一行。 (2)定义一个索引,用while语句把索引定位到第一行最后。 (3)因为每个请求报文第一行最后都是”http/1.x\r\n”,用strncmp比较http/和原字符串中倒数第十开始五个字符。 (4)如果返回0,则是http协议;函数返回1。 2、get_uri函数 原型:char *get_uri(char *req_header, char *uri_buf) 函数作用:从请求报文中获取URI(Uniform Resource Identifier) 函数逻辑: (1)定义索引,用while循环把索引定位到请求行第一个”/”;用一个变量记住此时位置。 (2)从上一个索引位置开始,用while循环,把索引定位到接下来的第一个空格“ ”处。 (3)这里判断URI是否过长和URI是否是“/”。 (4)URI是“/”,把"index.html"用strcpy拷贝到uri_buf中。 (5)否则,把对应req_header中的URI用strncpy拷贝到uri_buf中。 3、get_uri_status函数 原型:int get_uri_status(char *uri) 函数作用:获取URI的状态 Uri:uri_buf中的URI串。 函数逻辑: (1)用access函数判断URI中文件是否具有存取权限。 (2)用F_OK来判断文件是否存在,不存在返回-1。 (3)用R_OK来判断文件是否可以读取,不可读返回-1。 4、get_mime_type函数 函数原型:char *get_mime_type(char *uri) 作用:获取请求报文中MIME的类型。 Uri:uri_buf中的字符串。 函数逻辑: (1)定义索引,用while语句把索引定位到“.”的位置。 (2)计算URI总长度,用总长度减去索引,得到“.”之后后缀的长度;如果没有URI中没有“.”,说明是“/”。 (3)以上面的到的值作为switch语句的case,初步判断是什么MIME类型。 (4)在每个case中用strcmp比较从“.”开始的字符串的典型的MIME。 (5)得出具体MIME类型。(注意大小写和简写) 5、get_file_disk函数 函数原型:int get_file_disk(char *uri, unsigned char *file_buf) 函数作用是读取URI中的文件,把读到的文件拷贝到entity_buf中,如果成功返回读到字节数,失败返回-1 Uri:客户端连接中URI。 File_buf:读取文件并保存到这个buf中。 函数逻辑: (1)用open函数打开对应的文件,返回文件描述符。 (2)定义struct stat st,然后调用fstat函数用文件描述符获取文件的状态(主要是st_size)。 (3)判断文件大小是否会大于预定义最大文件大小,文件太大返回错误。 (4)用read函数通过open返回文件描述符读取文件到file_buf中。 6、set_error_information函数 函数原型:int set_error_information(unsigned char *send_buf, int errorno) 函数主要是应对处理错误,把相应的出错信息发送回客户端。 Send_buf:响应报文内容buf。 Errorno:相应的错误号。 函数逻辑: (1)通过传入errorno用Switch语句判断是哪种错误。 (2)此处只设置了一个FILE_NOT_FOUND错误,其他错误可以类似设置,后期可以补充完整。 (3)把响应报文状态行("HTTP/1.1 404 File Not Found\r\n")用memcpy到send_buf中。 (4)把服务器名和版本号等响应报文首部用memcpy到send_buf中。(用一个对send_buf的索引来控制copy) (5)把简单的html拷贝到send_buf中。( "\r\n\r\n404 File not found
Please check your url,and try it again!") 7、reply_normal_information函数 原型:int reply_normal_information(unsigned char *send_buf, unsigned char *file_buf, int file_size, char *mime_type) 函数主要作用是组合要回复给客户端的响应报文,返回响应报文总长度。 Send_buf:响应报文内容buf。 File_buf:根据客户端uri读取的文件内容buf。 File_size:读取文件的大小。 Mime_type:媒体类型 函数逻辑:(这一阶段主要用到的库函数:memset、memcpy) (1)把字符串"HTTP/1.1 200 OK\r\nServer:Mutu/Linux(0.1)\r\nDate:"拷贝到send_buf中。 (2)通过get_time_str函数获得时间字符串拷贝到send_buf中。(注意添加换行”\r\n”) (3)把宏定义的字符串ALLOW拷贝到send_buf,列出URL中可以使用的方法。 (4)把字符串"\r\nContent-Length:"拷贝到send_buf,然后把文档长度通过sprintf写入到字符串再拷贝到send_buf中。 (5)到这里请求行、首部都写到了send_buf里面,接下来写入两个换行(首部和主体间要有一个空行)--"\r\n\r\n"。 (6)把从URI中读到的文件再memcpy到send_buf中。 8、get_time_str函数 函数原型:char *get_time_str(char *time_buf) 函数作用:获取当前时间,把时间转换成字符串写到time_buf中。 函数逻辑: (1)先定义time_t类型的时间变量now_sec;存储从1970年到现在经过了多少秒。 (2)定义tm结构体*time_now,直接存储年月日的是一个结构。(年份是从1900年起至今多少年) (3)调用gmtime函数,把日期和时间转换为格林威治(GMT)时间。将参数time 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。 (4)调用asctime函数,把time_now指向的tm结构体中储存的时间转换为字符串,返回的字符串格式。(Wed Feb 13 15:46:11 2013) (5)把上一个函数返回的字符串strcpy到time_buf中。 9、http_session函数 函数原型:int http_session(int *connect_fd, struct sockaddr_in *client_addr) 作用:处理http会话。 Connect_fd:accept函数返回的套接口文件描述符。 Client_addr:accept函数返回的连接到服务器的客户端套接口地址结构。 函数逻辑: (1)定义select函数指示内核等待fd_set中多个事件中的任何一个发生,定义所需要的超时时间结构体、fd_set,并把accept返回的已连接套接字描述符加入到read_set中。(select函数只设置监听-read_set) int maxfd = *connect_fd + 1; fd_set read_set; FD_ZERO(&read_set); FD_SET(*connect_fd, &read_set); struct timeval timeout; timeout.tv_sec = TIME_OUT_SEC; timeout.tv_usec = TIME_OUT_USEC; (2)对select函数while死循环阻塞等待直到有时间发生,用switch函数盘断select返回值看有多少事件发生;用FD_ISSET判断传入的connect_fd是否被设置。如果是,就用recv接收套接口上的信息,保存在recv_buf中。(这里也包含错误处理,此处略过) (3)用is_http_protocol函数判断是否是http连接。 (4)用get_uri函数获取uri。 (5)用get_uri_status函数获取uri的状态。 (6)如果uri可读,调用reply_normal_information函数把响应报文头部及读到信息拷贝到send_buf中。 (7)如果uri不可读或不存在,调用set_error_information函数把响应报文头部及错误信息拷贝到send_buf中。 (8)调用send函数把send_buf内容通过connect_fd发送到客户端,完成http会话。 10、整个主函数及其他函数逻辑 (1)主函数开始先定义相应套接口描述字和套接口地址结构,并用bzero初始化。 (2)然后调用init_socket初始化服务器连接。(socket、setsockopt、bind、listen) (3)主进程阻塞在accept处,等待新的连接。 (4)新连接到来,调用fork函数创建子进程,子进程中调用http_session函数处理http会话。