Linux - 实现高并发HTTP服务器[只处理GET请求]
一、HTTP协议请求格式
1. 客户端请求
客户端发送一个HTTP请求到服务器的请求消息包括如下格式:请求行(request line)、请求头部(header)、空行和请求数据四部分组成
2. 服务器响应
服务器响应客户端的HTTP响应数据也由以下几部分组成:状态行、信息报头和响应正文
3. 项目中用到的响应代号
响应代号 | 代号描述 | 说明 |
---|---|---|
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;
}