基于reactor设计http服务器

目录

1. HTTP 简介

2. HTTP 工作原理

3. 基于reactor设计http服务器

3.1 解析http头

 3.2 封装http的响应

3.3 sendfile函数

4. 完整代码

5.  运行结果


1. HTTP 简介

        HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的 WWW 文件都必须遵守这个标准。 HTTP 是一个基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

2. HTTP 工作原理

        HTTP 协议工作于客户端-服务端架构上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。

  1. Web 服务器有: Apache 服务器, IIS 服务器(Internet Information Services)等。
  2. Web 服务器根据接收到的请求后,向客户端发送响应信息。
  3. HTTP 默认端口号为 80,但是你也可以改为 8080 或者其他端口。

3. 基于reactor设计http服务器

        reactor的原理与实现可以参考这里!!!

        主要实现俩部分,一部分是对http头的解析。另一部分是封装http的响应。

  

3.1 解析http头

//读行
int readline(char* allbuf, int idx, char* linebuf)
{
	int len = strlen(allbuf);

	for(; idx < len; idx++){
		if(allbuf[idx] == '\r' && allbuf[idx+1] == '\n')
			return idx+2;
		else
			*(linebuf++) = allbuf[idx];
	}
	return -1;
}

//解析http头
int nty_http_request(struct ntyevent * ev)
{
	char linebuff[1024] = {0};

	int idx = readline(ev->buffer, 0, linebuff);
	if(strstr(linebuff, "GET")){
		ev->method = HTTP_METHOD_GET;

		int i = 0;
		while(linebuff[sizeof("GET") + i] != ' ')i++;
		linebuff[sizeof("GET") + i] = '\0';

		sprintf(ev->resource, "%s/%s", HTTP_WEB_ROOT, linebuff+sizeof("GET "));
	
		printf("resource: %s\n", ev->resource);
	}
	else if(strstr(linebuff, "POST"))
	{
		ev->method = HTTP_METHOD_POST;
	}

	return 0;
}

 3.2 封装http的响应

//打包http头
int nty_http_response_get_method(struct ntyevent *ev) 
{
	//int filed = open()
#if 0
	int len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 78\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n"
"<html><head><title>0voice.king</title></head><body><h1>King</h1><body/></html>");

	ev->wlength = len;
#else
	int len;
	int filefd = open(ev->resource, O_RDONLY);
	if (filefd == -1) {

		len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 78\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n"
"<html><head><title>0voice.king</title></head><body><h1>King</h1><body/></html>");

		ev->wlength = len;
	
	} else {

		//获取文件大小
		struct stat stat_buf;
		fstat(filefd, &stat_buf);
		close(filefd);
#if 1
		len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size);
#else
	len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size);


#endif
		ev->wlength = len;

	}
	
#endif

	return len;
}


//数据打包
int nty_http_response(struct ntyevent* ev)
{
	// ev->method, ev->resouces
	if(ev->method == HTTP_METHOD_GET){
		nty_http_response_get_method(ev);
	}else if(ev->method == HTTP_METHOD_POST){

	}
	return 0;
}

3.3 sendfile函数

sendfile函数:

        sendfile函数是在两个文件描述符中直接传递数据(完全在内核中操作),从而避免了用户和内核之间的数据拷贝,所以效率很高,也被称之为零拷贝

sendfile函数用法

头文件:#include <sys/sendfile.h>

用法:ssize_t sendfile(int out_fd,int in_fd,off_t * offset,size_t count)

  1. out_fd:待写入内容的文件描述符,一般为accept的返回值
  2. in_fd:待读出内容的文件描述符,一般为open的返回值
  3. offset:指定从读入文件流的哪个位置开始读,如果为空,则使用读入文件流的默认位置,一般设置为NULL
  4. count:两文件描述符之间的字节数,一般给struct stat结构体的一个变量,在struct stat中可以设置文件描述符属性

注意:in_fd规定指向真实的文件,不能是socket等管道文件描述符,一般使open返回值,而out_fd则是socket描述符

sendfile的优点:是专门为网络上传输文件而设计的函数,效率高

4. 完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>

#include <sys/sendfile.h>



#define  BUFFER_LENGTH  	1024
#define  MAX_EPOLL_EVENTS   1024  //epoll事件数量
#define  RESOURCE_LENGTH	1024

#define  SERVER_PORT  		8888
#define  PORT_COUNT			100

typedef int NCALLBACK(int ,int ,void*);

#define HTTP_METHOD_GET		0
#define HTTP_METHOD_POST	1

#define HTTP_WEB_ROOT	"/home/kaka/share/html"

//管理每一个io fd的结构体
struct ntyevent{
	int fd; //io fd
	int events;
	void *arg;
	int (*callback)(int fd, int events, void* arg); //执行回调函数

	int status;	//判断是否已有事件
	char buffer[BUFFER_LENGTH]; //用户缓冲区

	char wbuffer[BUFFER_LENGTH]; //发送的数据
	
	int length; //用户缓冲区长度

	int wlength; //发送长度

	// http reqeust
	int method;
	char resource[RESOURCE_LENGTH];
	
};

//管理ntyevent fd的块
struct eventblock{
	struct eventblock* next; //指向ntyevent fd集合
	struct ntyevent* events; //指向下一个ntyevent fd的块
};

//reacotr结点
struct ntyreactor{
	int epfd;	//epoll fd
	int blkcnt;	//ntyevent fd的块 计数

	struct eventblock* evblks;	//指向ntyevent fd的块头结点
};

int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);
int accept_cb(int fd, int events, void* arg);

struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd);

//io fd结构体设置
void nty_event_set(struct ntyevent* ev, int fd, NCALLBACK callback, void* arg)
{
	ev->fd = fd;
	ev->callback = callback;
	ev->events = 0;
	ev->arg = arg;

	return ;
}

//io fd add
int nty_event_add(int epfd, int events, struct ntyevent *ev)
{
	struct epoll_event ep_ev = {0, {0}};
	ep_ev.data.ptr = ev;				//io fd结构体
	ep_ev.events = ev->events = events; //需要检测的fd事件

	int op; //操作类型
	if(ev->status == 1){
		op = EPOLL_CTL_MOD; //修改
	}else{
		op = EPOLL_CTL_ADD; //添加
		ev->status = 1;		//标志已经添加
	}
		
	if(epoll_ctl(epfd, op, ev->fd, &ep_ev) <0 ){ //绑定
		printf("event add failed [fd=%d], events[%d]\n", ev->fd, events);
		return -1;
	}

	return 0;
}

//io fd del
int nty_event_del(int epfd, struct ntyevent* ev)
{
	struct epoll_event ep_ev = {0, {0}};
	if(ev->status != 1){ //没有添加过检测的fd事件
		return -1;
	}

	ep_ev.data.ptr = ev;
	ev->status = 0; //标志未添加
	epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev);
	return 0;
}

// request
// location  /0voice/king/index.html HTTP/1.1

//读行
int readline(char* allbuf, int idx, char* linebuf)
{
	int len = strlen(allbuf);

	for(; idx < len; idx++){
		if(allbuf[idx] == '\r' && allbuf[idx+1] == '\n')
			return idx+2;
		else
			*(linebuf++) = allbuf[idx];
	}
	return -1;
}

//解析http头
int nty_http_request(struct ntyevent * ev)
{
	char linebuff[1024] = {0};

	int idx = readline(ev->buffer, 0, linebuff);
	if(strstr(linebuff, "GET")){
		ev->method = HTTP_METHOD_GET;

		int i = 0;
		while(linebuff[sizeof("GET") + i] != ' ')i++;
		linebuff[sizeof("GET") + i] = '\0';

		sprintf(ev->resource, "%s/%s", HTTP_WEB_ROOT, linebuff+sizeof("GET "));
	
		printf("resource: %s\n", ev->resource);
	}
	else if(strstr(linebuff, "POST"))
	{
		ev->method = HTTP_METHOD_POST;
	}

	return 0;
}

//打包http头
int nty_http_response_get_method(struct ntyevent *ev) 
{
	//int filed = open()
#if 0
	int len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 78\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n"
"<html><head><title>0voice.king</title></head><body><h1>King</h1><body/></html>");

	ev->wlength = len;
#else
	int len;
	int filefd = open(ev->resource, O_RDONLY);
	if (filefd == -1) {

		len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 78\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n"
"<html><head><title>0voice.king</title></head><body><h1>King</h1><body/></html>");

		ev->wlength = len;
	
	} else {

		//获取文件大小
		struct stat stat_buf;
		fstat(filefd, &stat_buf);
		close(filefd);
#if 1
		len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size);
#else
	len = sprintf(ev->wbuffer, 
	"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: image/png\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size);


#endif
		ev->wlength = len;

	}
	
#endif

	return len;
}


//数据打包
int nty_http_response(struct ntyevent* ev)
{
	// ev->method, ev->resouces
	if(ev->method == HTTP_METHOD_GET){
		nty_http_response_get_method(ev);
	}else if(ev->method == HTTP_METHOD_POST){

	}
	return 0;
}

//recv回调
int recv_cb(int fd, int events, void* arg)
{
	struct ntyreactor* reactor = (struct ntyreactor*)arg;
	struct ntyevent* ev = ntyreactor_idx(reactor, fd);	

	if(ev == NULL)return -1;

	int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);
	nty_event_del(reactor->epfd, ev);

	if (len > 0) {
		
		ev->length = len;
		ev->buffer[len] = '\0';

		//printf("recv [%d]:%s\n", fd, ev->buffer);

		nty_http_request(ev); //http解析

		//将fd 设置为发送事件
		nty_event_set(ev, fd, send_cb, reactor);
		nty_event_add(reactor->epfd, EPOLLOUT, ev);
		
		
	} else if (len == 0) {  //客户端断开连接

		nty_event_del(reactor->epfd, ev);
		printf("recv_cb --> disconnect\n");
		close(ev->fd);
		 
	} else { //返回错误

		if (errno == EAGAIN && errno == EWOULDBLOCK) { //
			
		} else if (errno == ECONNRESET){
			nty_event_del(reactor->epfd, ev);
			close(ev->fd);
		}
		printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
		
	}

	return len;
}


//send回调
int send_cb(int fd, int events, void* arg)
{
	struct ntyreactor* reactor = (struct ntyreactor*)arg;
	struct ntyevent* ev = ntyreactor_idx(reactor, fd);

	if (ev == NULL) return -1;

	nty_http_response(ev); //encode

	int len = send(fd, ev->wbuffer, ev->wlength, 0);
	if (len > 0) {
		//printf("send[fd=%d], [%d]%s\n", fd, len, ev->wbuffer);

		int filefd = open(ev->resource, O_RDONLY);
		if(filefd < 0) return 0;

		struct stat stat_buf;
		fstat(filefd, &stat_buf);

		int flag = fcntl(fd, F_GETFL, 0);
		flag &= ~O_NDELAY;
		fcntl(fd, F_SETFL, flag);

		//sendfile 需要做成阻塞io
		int ret = sendfile(fd, filefd, NULL, stat_buf.st_size);
		if (ret == -1) {
			printf("sendfile: errno: %d\n", errno);
		}

		flag |= O_NONBLOCK;
		fcntl(fd, F_SETFL, flag);

		close(filefd);

		send(fd, "\r\n", 2, 0);

		//发送后,将fd设置为接收事件
		nty_event_del(reactor->epfd, ev);
		nty_event_set(ev, fd, recv_cb, reactor);
		nty_event_add(reactor->epfd, EPOLLIN, ev);
		
	} else { //发送失败

		nty_event_del(reactor->epfd, ev);
		close(ev->fd);

		printf("send[fd=%d] error %s\n", fd, strerror(errno));

	}

	return len;
}


//客户端接入回调
int accept_cb(int fd, int events, void* arg)
{
	struct ntyreactor *reactor = (struct ntyreactor*)arg;
	if (reactor == NULL) return -1;

	struct sockaddr_in client_addr;
	socklen_t len = sizeof(client_addr);

	int clientfd;

	//客户端接入
	if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) {
		if (errno != EAGAIN && errno != EINTR) {
			
		}
		printf("accept: %s\n", strerror(errno));
		return -1;
	}

	//设置非阻塞fd
	int flag = 0;
	if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) {
		printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
		return -1;
	}

	struct ntyevent *event = ntyreactor_idx(reactor, clientfd);

	if (event == NULL) return -1;

	//将该fd设置为recv
	nty_event_set(event, clientfd, recv_cb, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);

	printf("new connect [%s:%d], pos[%d]\n", 
		inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);

	return 0;
}

//创建socket监听
int init_sock(short port)
{
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	fcntl(fd, F_SETFL, O_NONBLOCK);

	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(port);

	bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));

	if (listen(fd, 20) < 0) {
		printf("listen failed : %s\n", strerror(errno));
		return -1;
	}

	printf("listen server port : %d\n", port);
	return fd;
}


//reactor扩展大小
int ntyreactor_alloc(struct ntyreactor* reactor)
{
	if(reactor == NULL) return -1;
	if(reactor->evblks == NULL) return -1;

	struct eventblock* blk = reactor->evblks; //块的头结点

	//找尾节点
	while(blk->next != NULL){  //找到尾节点
		blk = blk->next;
	}

	struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
	if (evs == NULL) {
		printf("ntyreactor_alloc ntyevent failed\n");
		return -2;
	}
	memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));

	struct eventblock *block = malloc(sizeof(struct eventblock));
	if (block == NULL) {
		printf("ntyreactor_alloc eventblock failed\n");
		return -3;
	}
	//io fd集合连接成块
	block->events = evs;
	block->next = NULL;

	//指向新块
	blk->next = block;
	reactor->blkcnt ++;

	return 0;
}

//根据io fd来找fd结构体
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd) {
	if (reactor == NULL) return NULL;
	if (reactor->evblks == NULL) return NULL;

	int blkidx = sockfd / MAX_EPOLL_EVENTS; //在哪一个块
	while (blkidx >= reactor->blkcnt) {		//大小不够扩容
		ntyreactor_alloc(reactor);
	}

	int i = 0;
	struct eventblock *blk = reactor->evblks; //头结点块
	while (i++ != blkidx && blk != NULL) {    //找到所在的块
		blk = blk->next;
	}

	return &blk->events[sockfd % MAX_EPOLL_EVENTS]; //返回fd结构体
}

//reactor初始化
int ntyreactor_init(struct ntyreactor* reactor)
{
	if(reactor == NULL) return -1;
	memset(reactor, 0, sizeof(struct ntyreactor));

	reactor->epfd = epoll_create(1); 
	if (reactor->epfd <= 0) {
		printf("create epfd in %s err %s\n", __func__, strerror(errno));
		return -2;
	}

	//创建第一个块
	struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
	if (evs == NULL) {
		printf("create epfd in %s err %s\n", __func__, strerror(errno));
		close(reactor->epfd);
		return -3;
	}
	memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));

	struct eventblock *block = malloc(sizeof(struct eventblock));
	if (block == NULL) {
		free(evs);
		close(reactor->epfd);
		return -3;
	}
	block->events = evs;
	block->next = NULL;

	reactor->evblks = block;
	reactor->blkcnt = 1;

	return 0;
}

//销毁reactor
int ntyreactor_destory(struct ntyreactor* reactor)
{
	close(reactor->epfd);

	struct eventblock *blk = reactor->evblks;
	struct eventblock *blk_next;
	while (blk != NULL) {
		blk_next = blk->next;

		free(blk->events);
		free(blk);
		
		blk = blk_next;
	}

	return 0;
}

//初始化接收连接socket
int ntyreactor_addlistener(struct ntyreactor* reactor, int sockfd, NCALLBACK *acceptor){
	if (reactor == NULL) return -1;
	if (reactor->evblks == NULL) return -1;

	struct ntyevent* event = ntyreactor_idx(reactor, sockfd);
	if (event == NULL) return -1;

	nty_event_set(event, sockfd, acceptor, reactor);
	nty_event_add(reactor->epfd, EPOLLIN, event);

	return 0;
}

//reactor事件循环
int ntyreactor_run(struct ntyreactor* reactor)
{
	if (reactor == NULL) return -1;
	if (reactor->epfd < 0) return -1;
	if (reactor->evblks == NULL) return -1;
	
	struct epoll_event events[MAX_EPOLL_EVENTS+1];
	
	int checkpos = 0, i;

	while(1){
		int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
		if (nready < 0) {
			printf("epoll_wait error, exit\n");
			continue;
		}

		for(i = 0;i < nready; i++){
			struct ntyevent* ev = (struct ntyevent*)events[i].data.ptr; //发生事件的io fd结构体

			if((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)){
				ev->callback(ev->fd, events[i].events, ev->arg);
			}
			if((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)){
				ev->callback(ev->fd, events[i].events, ev->arg);
			}
		
		}

	}

}

int main(int argc, char *argv[]) {

	struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct ntyreactor));
	ntyreactor_init(reactor);

	//起始的端口号
	unsigned short port = SERVER_PORT;
	if (argc == 2) {
		port = atoi(argv[1]);
	}

	int i = 0;
	int sockfds[PORT_COUNT] = {0};

	for (i = 0;i < PORT_COUNT;i ++) {
		sockfds[i] = init_sock(port+i);
		ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
	}

	ntyreactor_run(reactor);

	ntyreactor_destory(reactor);
	
	for (i = 0;i < PORT_COUNT;i ++) {
		close(sockfds[i]);
	}
	free(reactor);

	return 0;
}






5. 运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值