客户端发送一个HTTP请求到服务器的请求消息包括如下格式:请求行(request line)、请求头部(header)、空行和请求数据四部分组成
服务器响应客户端的HTTP响应数据也由以下几部分组成:状态行、信息报头和响应正文
响应代号 | 代号描述 | 说明 |
---|---|---|
200 | OK | 服务器上存在请求的内容,并可以响应给客户端 |
400 | BAD REQUEST | 客户端发送的请求格式有问题 |
404 | NOT FOUND | 请求的内容不存在 |
500 | Internal Server Error | 服务器已收到请求,但因自身的问题无法响应 |
501 | Method Not Implemented | 客户端请求异常,方法有问题 |
#include <stdio.h> #include <errno.h> #include <ctype.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <sys/stat.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/socket.h> #define SERVER_PORT 80 #define FILE_PATH "./resources_file/" #define DEBUG true int init_server(); int accept_connect(int _ServerSock); void* process_http_request(void* _pClientSock); void ok_response(int _ClientSock, const char* url); // 200 void bad_request(int _ClientSock); // 400 void not_found(int _ClientSock); // 404 void inner_error(int _ClientSock); // 500 void unimplemented(int _ClientSock); // 501 int header(int _ClientSock, FILE* _File); void cat(int _ClientSock, FILE* _File); int get_line(int _ClientSock, char* _Buffer, size_t _BufferSize); int main() { int sock = init_server(); pthread_t pthread_id = 0; if (sock < 0) { printf("init_server() - failed! reason: %s\n", strerror(-sock)); exit(-1); } printf("init_server() - success! wait connection...\n"); int done = 1; while (done) { int client = accept_connect(sock); if (client < 0) { fprintf(stderr, "accept_connect() - failed! reason: %s\n", strerror(-client)); exit(-2); } // 串行处理 // process_http_request(&client); // 并行处理 pthread_create(&pthread_id, NULL, process_http_request, &client); } close(sock); return 0; } /**************************************************************** * 功能:初始化服务器 * 参数:无 * 返回:成功 - 返回创建的socket编号 * 失败 - 返回(-errno) *****************************************************************/ int init_server() { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { return -errno; } struct sockaddr_in in; bzero(&in, sizeof(in)); in.sin_family = AF_INET; in.sin_addr.s_addr = htonl(INADDR_ANY); in.sin_port = htons(SERVER_PORT); int ret = bind(sock, (struct sockaddr*)(&in), sizeof(in)); if (ret < 0) { return -errno; } ret = listen(sock, 128); if (ret < 0) { return -errno; } return sock; } /**************************************************************** * 功能:接收客户端连接 * 参数: * _ServerSock - 服务器sock描述符 * 返回:成功 - 返回与客户端建立的sock描述符 * 失败 - 返回-1 * 说明:如果没有连接,则阻塞 *****************************************************************/ int accept_connect(int _ServerSock) { struct sockaddr_in in; bzero(&in, sizeof(in)); socklen_t in_len = sizeof(in); int sock = accept(_ServerSock, (struct sockaddr*)(&in), &in_len); if (sock < 0) { fprintf(stderr, "accept() - failed! reason: %s\n", strerror(errno)); return -1; } #if DEBUG == true char buffer[64] = { 0 }; printf("accept() - success! Client Info: ip addr - %s, port - %d\n", inet_ntop(AF_INET, &in.sin_addr.s_addr, buffer, sizeof(buffer)), ntohs(in.sin_port)); #endif // DEBUG == true return sock; } /**************************************************************** * 功能:处理HTTP请求 * 参数: * _pClientSock - 客户端sock描述符指针 * 返回:NULL *****************************************************************/ void* process_http_request(void* _pClientSock) { int sock = *((int*)_pClientSock); char buffer[1024] = { 0 }; // 用于读取行 char method[64] = { 0 }; // 请求方法,只处理[GET]方法 char filename[256] = { 0 }; // 文件名 char url[512] = { 0 }; // 文件路径,通过filename整合 int len = get_line(sock, buffer, sizeof(buffer)); if (len < 0) { #if DEBUG == true printf("process_http_request() - failed! reason: %s\n", strerror(-len)); #endif // DEBUG == true bad_request(sock); close(sock); return NULL; } int buffer_index = 0, index = 0; // 获取请求方法 while ((!isspace(buffer[buffer_index])) && (index < sizeof(method) - 1)) { method[index++] = buffer[buffer_index++]; } method[index] = '\0'; // 非GET方法处理 if (strncasecmp(method, "GET", 3) != 0) { #if DEBUG == true printf("process_http_request() - !GET\n"); #endif // DEBUG == true do { len = get_line(sock, buffer, sizeof(buffer)); #if DEBUG == true printf("[!GET] data: %s\n", buffer); #endif // DEBUG == true } while (len > 0); unimplemented(sock); close(sock); return NULL; } while (isspace(buffer[buffer_index++])) {} // 过滤空格 // 获取filename index = 0; while ((!isspace(buffer[buffer_index])) && (index < sizeof(filename) - 1)) { filename[index++] = buffer[buffer_index++]; } // 处理filename中的'?'。例:index.html?username=111 char* p = strchr(filename, '?'); if (p) { *p = '\0'; #if DEBUG == true printf("process_http_request() - found '?' in filename, after process: %s\n", filename); #endif // DEBUG == true } // 将filename整合成url int url_len = snprintf(url, sizeof(url), FILE_PATH "%s", filename); do { len = get_line(sock, buffer, sizeof(buffer)); #if DEBUG == true printf("data: %s\n", buffer); #endif // DEBUG == true } while (len > 0); struct stat st; if (stat(url, &st) < 0) { #if DEBUG == true printf("process_http_request() - The requested page does not exist!\n"); #endif // DEBUG == true not_found(sock); } else { ok_response(sock, url); } close(sock); return NULL; } /**************************************************************** * 功能:处理HTTP请求 * 参数: * _pClientSock - 客户端sock描述符指针 * 返回:NULL *****************************************************************/ void ok_response(int _ClientSock, const char* url) { FILE* file = fopen(url, "r"); if (!file) { #if DEBUG == true printf("ok_response() - failed! reason: %s\n", strerror(errno)); #endif // DEBUG == true not_found(_ClientSock); return; } // 1. 发送头部数据 int ret = header(_ClientSock, file); // 2. 发送文件数据 if (!ret) { cat(_ClientSock, file); } fclose(file); } /**************************************************************** * 功能:客户端发送的请求格式有问题时的响应[400] * 参数: * _ClientSock - 客户端sock描述符 * 返回:无 *****************************************************************/ void bad_request(int _ClientSock) { char reply[] = "HTTP/1.0 400 Method Not Implemented\r\n" "Content - Type: text/html\r\n" "\r\n" "<HTML>\r\n" "<HEAD>\r\n" "<TITLE>BAD REQUEST</TITLE>\r\n" "</HEAD>\r\n" "<BODY>\r\n" "<P>Your browser send a bad request!\r\n" "</BODY>\r\n" "</HTML>\r\n"; int len = write(_ClientSock, reply, strlen(reply)); if (len < 0) { fprintf(stderr, "bad_request() write failed! reason: %s\n", strerror(errno)); return; } #if DEBUG == true printf("bad_request()[%d]: %s\n", len, reply); #endif // DEBUG == true } /**************************************************************** * 功能:客户端请求的数据不存在时的响应[404] * 参数: * _ClientSock - 客户端sock描述符 * 返回:无 *****************************************************************/ void not_found(int _ClientSock) { char reply[] = "HTTP/1.0 404 NOT FOUND\r\n" "Content - Type: text/html\r\n" "\r\n" "<HTML>\r\n" "<HEAD>\r\n" "<TITLE>404 NOT FOUND</TITLE>\r\n" "</HEAD>\r\n" "<BODY>\r\n" "<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n" "</BODY>\r\n" "</HTML>\r\n"; int len = write(_ClientSock, reply, strlen(reply)); if (len < 0) { fprintf(stderr, "not_found() write failed! reason: %s\n", strerror(errno)); return; } #if DEBUG == true printf("not_found()[%d]: %s\n", len, reply); #endif // DEBUG == true } /**************************************************************** * 功能:服务器内部错误时的响应[500] * 参数: * _ClientSock - 客户端sock描述符 * 返回:无 *****************************************************************/ void inner_error(int _ClientSock) { char reply[] = "HTTP/1.0 500 Internal Server Error\r\n" "Content - Type: text/html\r\n" "\r\n" "<HTML lang=\"zh-CN\">\r\n" "<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n" "<HEAD>\r\n" "<TITLE>Inner Error!</TITLE>\r\n" "</HEAD>\r\n" "<BODY>\r\n" "<P>服务器内部出错!\r\n" "</BODY>\r\n" "</HTML>\r\n"; int len = write(_ClientSock, reply, strlen(reply)); if (len < 0) { fprintf(stderr, "bad_request() write failed! reason: %s\n", strerror(errno)); return; } #if DEBUG == true printf("bad_request()[%d]: %s\n", len, reply); #endif // DEBUG == true } /**************************************************************** * 功能:客户端的请求为非GET时的响应[501] * 参数: * _ClientSock - 客户端sock描述符 * 返回:无 *****************************************************************/ void unimplemented(int _ClientSock) { char reply[] = "HTTP/1.0 501 Method Not Implemented\r\n" "Content - Type: text/html\r\n" "\r\n" "<HTML>\r\n" "<HEAD>\r\n" "<TITLE>Method Not Implemented</TITLE>\r\n" "</HEAD>\r\n" "<BODY>\r\n" "<P>HTTP request method not supported.\r\n" "</BODY>\r\n" "</HTML>\r\n"; int len = write(_ClientSock, reply, strlen(reply)); if (len < 0) { fprintf(stderr, "unimplemented() write failed! reason: %s\n", strerror(errno)); return; } #if DEBUG == true printf("unimplemented()[%d]: %s\n", len, reply); #endif // DEBUG == true } /**************************************************************** * 功能:发送HTTP响应端头部 * 参数: * _ClientSock - 客户端sock描述符 * _File - 待发送数据的文件指针 * 返回:成功返回0 失败返回-1 *****************************************************************/ int header(int _ClientSock, FILE* _File) { struct stat st; int fileid = 0; char tmp[128] = { 0 }; char buffer[1024] = { 0 }; strcpy(buffer, "HTTP/1.0 200 OK\r\n"); strcat(buffer, "Server: Beauty Server\r\n"); strcat(buffer, "Content-Type: text/html\r\n"); strcat(buffer, "Connection: Close\r\n"); fileid = fileno(_File); if (fstat(fileid, &st) == -1) { #if DEBUG == true printf("header() - failed! reason: %s\n", strerror(errno)); #endif // DEBUG == true inner_error(_ClientSock); return -1; } int content_len_len = snprintf(tmp, sizeof(tmp), "Content-Length:%ld\r\n\r\n", st.st_size); strcat(buffer, tmp); #if DEBUG == true printf("header() - data: %s\n", buffer); #endif // DEBUG == true if (send(_ClientSock, buffer, strlen(buffer), 0) < 0) { fprintf(stderr, "header() - send failed! reason: %s\n", strerror(errno)); return -1; } return 0; } /**************************************************************** * 功能:发送HTTP响应端数据 * 参数: * _ClientSock - 客户端sock描述符 * _File - 待发送数据的文件指针 * 返回:无 *****************************************************************/ void cat(int _ClientSock, FILE* _File) { char buffer[1024] = { 0 }; fgets(buffer, sizeof(buffer), _File); while (!feof(_File)) { strcat(buffer, "\r\n"); int len = write(_ClientSock, buffer, strlen(buffer)); if (len < 0) { fprintf(stderr, "send body error. reason: %s\n", strerror(errno)); break; } fgets(buffer, sizeof(buffer), _File); } } /**************************************************************** * 功能:从 _ClientSock 读取一行数据 * 参数: * _ClientSock - 客户端sock描述符 * _Buffer - 待写入一行数据的首地址 * _BufferSize - _Buffer 大小 * 返回:成功 - 返回读取的字节数 * 失败 - 返回-1 *****************************************************************/ int get_line(int _ClientSock, char* _Buffer, size_t _BufferSize) { size_t count = 0; char ch = '0'; while ((count < _BufferSize - 1) && (ch != '\0')) { int len = read(_ClientSock, &ch, 1); if (len == 1) { // 读取成功 if (ch == '\r') { continue; } else if (ch == '\n') { break; } _Buffer[count++] = ch; } else if (len == -1) { // 读取失败 #if DEBUG == true printf("get_line() - Read Error! reason: %s\n", strerror(errno)); #endif // DEBUG == true count = -1; break; } else { // len == 0,客户端关闭了sock连接 #if DEBUG == true printf("get_line() - The client closed the sock connection! reason: %s\n", strerror(errno)); #endif // DEBUG == true count = -1; break; } } if (count >= 0 && count < _BufferSize) { _Buffer[count] = '\0'; } return count; }