Linux教程

Linux - 实现高并发HTTP服务器(只处理GET请求)

本文主要是介绍Linux - 实现高并发HTTP服务器(只处理GET请求),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Linux - 实现高并发HTTP服务器[只处理GET请求]

    • 一、HTTP协议请求格式
      • 1. 客户端请求
      • 2. 服务器响应
      • 3. 项目中用到的响应代号
    • 二、项目实现

一、HTTP协议请求格式

1. 客户端请求

客户端发送一个HTTP请求到服务器的请求消息包括如下格式:请求行(request line)、请求头部(header)、空行和请求数据四部分组成
请求报文的一般格式

2. 服务器响应

服务器响应客户端的HTTP响应数据也由以下几部分组成:状态行、信息报头和响应正文
服务器响应的一般格式

3. 项目中用到的响应代号

响应代号代号描述说明
200OK服务器上存在请求的内容,并可以响应给客户端
400BAD REQUEST客户端发送的请求格式有问题
404NOT FOUND请求的内容不存在
500Internal Server Error服务器已收到请求,但因自身的问题无法响应
501Method 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;
}

这篇关于Linux - 实现高并发HTTP服务器(只处理GET请求)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!