博客园  :: 首页  :: 新随笔  :: 管理

2.2.2 http服务器的实现

Posted on 2023-03-07 04:25  wsg_blog  阅读(186)  评论(0编辑  收藏  举报

Linux C/C++服务器

http服务器的实现

http服务器说的是在reactor网络io框架基础上加上http协议,实现可与浏览器交互的后端服务,reactor在之前有介绍,下面我们先来看看http协议都有哪些东西

http协议

http消息分两部分,一个是客户端请求消息,一个是服务器响应消息

http客户端请求消息

客户端发送一个http请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行(/r/n)和请求数据四部分组成


客户端请求示例:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi

http服务器响应消息

HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文


服务端响应示例:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

结果:网页端显示

Hello World! My payload includes a trailing CRLF.

关于http请求方法、响应头信息、状态码这些详细信息,在这个文档中都有

reactor服务端框架

框架主要由这四个接口组成,1.ntyreactor_run() 保证服务端7*24小时运行 2.accpet_cb() 与客户端建立连接 3.recv_cb() 接受客户端http请求 4.send_cb() 发送http响应
其中accept_cb()可以做ip限制、负载均衡的场景中使用

reactor框架

reactor框架在之前有介绍,这里对之前版本的reactor代码做了部分优化,让它看起来更漂亮更健壮一些;

event事件中新增回调函数及http方法

struct ntyevent {
	int 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;
	//long last_active;

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

event回调函数设置接口,在send_cb和recv_cb接口的最后调用它

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;
	//ev->last_active = time(NULL);
	return ;	
}

在epoll_wait事件触发后调用相应的回调函数

//ntyreactor_run
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;

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

	}

那么剩下的就只有http协议如何去解析recv_cb()和回传的问题send_cb()

recv_cb()

parse

// connection
// sock_item --> fd, rbuffer, wbuffer, clientaddr
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); // parser http hdr
		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_cb()

responce

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("resource: %s\n", ev->resource);

		int filefd = open(ev->resource, O_RDONLY);
		//if (filefd < 0) return -1;
		
		struct stat stat_buf;
		fstat(filefd, &stat_buf);
		
		int flag = fcntl(fd, F_GETFL, 0);
		flag &= ~O_NONBLOCK;
		fcntl(fd, F_SETFL, flag);
			
		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);

		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;
}

代码实现,比较简单,直接看源码就好