Linux网络:基于reactor(epoll)实现Webserver

1.基于reactor模型实现网络IO底层通信

          网络通信代码见上篇:Linux网络:reactor模型封装epoll:学习笔记2-CSDN博客

2.Webserver功能

  1.   接受来自网络端的连接请求并解析;
  2.   对请求做响应,处理相应的请求业务,回发数据给网络端。

3.Webserver代码

        server端代码:

#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <pthread.h>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include"server.h"


#define TIME_SUB_MS(tv1, tv2)  ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

int init_server(unsigned short port);

int accept_connect(int fd);

int recv_cb(int fd);

int send_cb(int fd);

int set_event(int fd ,int event,int tag);



int epfd =0;
int opt=-1;

struct conn_item connlist[1048576]={0};


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

	int nport=20;
	unsigned short port = 2048;
	epfd =epoll_create(1);

	for(int i=0;i<nport;i++){
		int sockfd=init_server(port+i);//一个端口只能绑定一个客户端?
		connlist[sockfd].fd=sockfd;
		connlist[sockfd].recv_t.accept_callback=accept_connect;
		set_event(sockfd, EPOLLIN, 1);
		
	}


	if(argc!=0) opt=atoi(argv[1]);

	
	//printf("opt : %d",opt);

	//gettimeofday(&nowtimes, NULL);
	struct epoll_event events[1024] = {0};

	while(1){
		
		int nready =epoll_wait(epfd,events,1024,-1);

		for(int i=0;i<nready;i++){
			
			int connfd=events[i].data.fd;

			if(events[i].events&EPOLLIN){
				
				int count =connlist[connfd].recv_t.recv_callback(connfd);
				//printf("recv count: %d <-- buffer: %s\n", count, connlist[connfd].rbuffer);
			
			}

			if(events[i].events&EPOLLOUT){

			   //printf("send --> buffer: %s\n",  connlist[connfd].wbuffer);
			   int count =connlist[connfd].send_callback(connfd);
			   //printf("send --> buffer: %s\n",  connlist[connfd].wbuffer);
			}
		}


	}
	
	
    return 0;

}


int init_server(unsigned short port){

	int sockfd =socket(AF_INET,SOCK_STREAM,0);

	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(struct sockaddr_in));

	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
	serveraddr.sin_port=htons(port);
	if(-1==bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(struct sockaddr))){
		perror("bind error");
		return -1;
	}

	listen(sockfd,10);

	return sockfd;
}

int set_event(int fd ,int event,int tag){

	if(tag){
		struct epoll_event ev;
		ev.events=event;
		ev.data.fd=fd;
		epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
	}else{
		struct epoll_event ev;
		ev.events=event;
		ev.data.fd=fd;
		epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);//not del?
	}
	return 0;	
}

int event_register(int fd ,int event){
    
    if(fd <0)return -1;

    connlist[fd].fd=fd;
	
	memset(connlist[fd].rbuffer, 0, BUFFER_LENGTH);
	connlist[fd].rlen = 0;
	memset(connlist[fd].wbuffer, 0, BUFFER_LENGTH);
	connlist[fd].wlen = 0;

	connlist[fd].recv_t.recv_callback=recv_cb;
	connlist[fd].send_callback=send_cb;

    set_event( fd, event, 1);
    return 0;
}


int accept_connect(int fd){

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

	int clientfd=accept(fd,(struct sockaddr *)&clientaddr,&len);

	if(clientfd<0){
       // printf("accept errno: %d --> %s\n", errno, strerror(errno));
		return -1;
	}
    event_register(clientfd,EPOLLIN);

	// if ((clientfd % 1000) == 999) {

	//     struct timeval tv_cur;
	//     gettimeofday(&tv_cur, NULL);
	//     int time_used = TIME_SUB_MS(tv_cur, nowtimes);
	//     memcpy(&nowtimes, &tv_cur, sizeof(struct timeval));
    
	//     //printf("fd : %d, time_used: %d\n", clientfd, time_used);
	// }
   
	return 0;
	
}

int recv_cb(int fd){//需要注意的是每个连接端口对应一个fd

	
	memset(connlist[fd].rbuffer,0,BUFFER_LENGTH);

	int count=recv(fd,connlist[fd].rbuffer,BUFFER_LENGTH,0);


	if(count ==0){

        //printf("client disconnect: %d\n", fd);
		//printf("disconnect\n");
        close(fd);
		epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
	
		
		return 0;
	}else if(count<0){
        //printf("count: %d, errno: %d, %s\n", count, errno, strerror(errno));
		close(fd);
		epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

		return 0;
    }

   
     connlist[fd].rlen = count;	
	//memcpy(connlist[fd].wbuffer, connlist[fd].rbuffer, count);
	
	connlist[fd].wlen = connlist[fd].rlen;

#if ENABLE_HTTP_RESPONSE
    http_request(&connlist[fd]);
#elif
   
	memcpy(connlist[fd].wbuffer, connlist[fd].rbuffer, connlist[fd].rlen);	
#endif



	set_event(fd, EPOLLOUT, 0);

	
	return count;
}

int send_cb(int fd){

    int count=0;

#if ENABLE_HTTP_RESPONSE
    count=http_response(&connlist[fd],opt);

#elif
	char *buffer = connlist[fd].wbuffer;
	int wlen= connlist[fd].wlen;

	int count = send(fd, buffer, wlen, 0);
	
#endif

#if ENABLE_HTTP_RESPONSE
		if (connlist[fd].states == 1) {
		//printf("SEND: %s\n", connlist[fd].wbuffer);
		count = send(fd, connlist[fd].wbuffer, connlist[fd].wlen, 0);
		set_event(fd, EPOLLOUT, 0);
	} else if (connlist[fd].states == 2) {
		set_event(fd, EPOLLOUT, 0);
	} else if (connlist[fd].states == 0) {

		if (connlist[fd].wlen != 0) {
			count = send(fd, connlist[fd].wbuffer, connlist[fd].wlen, 0);
		}
		
		set_event(fd, EPOLLIN, 0);
	}

#else
    if (connlist[fd].wlen != 0)
    count = send(fd, &connlist[fd].wbuffer, connlist[fd].wlen, 0);
	set_event(fd, EPOLLIN, 0);
#endif
	

	return count;
}

       业务端代码:
//头文件


#ifndef __SERVER_H__
#define __SERVER_H__

#define ENABLE_HTTP_RESPONSE	1

#define BUFFER_LENGTH 1024



//struct timeval nowtimes;
typedef int (*RCALLBACK)(int fd);
struct conn_item{

	int fd;

	char rbuffer[BUFFER_LENGTH];
	int  rlen;

	char wbuffer[BUFFER_LENGTH];
	int  wlen;

	union{
		RCALLBACK accept_callback;
		RCALLBACK recv_callback;

	}recv_t;
	RCALLBACK send_callback;
	
    int states;//状态机控制回调函数的调用0接请求,1接收数据,2接收完成
	
};



int http_request(struct conn_item *conn);

int http_response(struct conn_item *conn,int opt);



#endif





#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/sendfile.h>
#include <errno.h>


#include"server.h"



typedef struct response_s{
    int str ;
    int html;
    int png;
    
}response_t;

//response_t rpack={1,0,0};//不同的业务的处理,

int http_request(struct conn_item *conn){
    
    //printf("request: %s\n", conn->rbuffer);
    memset(conn->wbuffer,0,BUFFER_LENGTH);
    
    conn->wlen=0;
    conn->states=0;

	return 0;

}



int del_string(struct conn_item *conn){

	
	conn->wlen = sprintf(conn->wbuffer, 
		"HTTP/1.1 200 OK\r\n"
		"Content-Type: text/html\r\n"
		"Accept-Ranges: bytes\r\n"
		"Content-Length: 82\r\n"
		"Date: Tue, 30 Apr 2024 13:16:46 GMT\r\n\r\n"
		"<html><head><title>0voice.Lugy</title></head><body><h1>hello friends !</h1></body></html>\r\n\r\n");

    return conn->wlen;

}

int del_html(struct conn_item *conn){

	
	int filefd = open("index.html", O_RDONLY);

	
	struct stat file_buf;
	fstat(filefd, &file_buf);
	
	if(conn->states==0){
		conn->wlen = sprintf(conn->wbuffer, 
		"HTTP/1.1 200 OK\r\n"
		"Content-Type: text/html\r\n"
		"Accept-Ranges: bytes\r\n"
		"Content-Length: %ld\r\n"
		"Date: Tue, 30 Apr 2024 13:16:46 GMT\r\n\r\n", 
		file_buf.st_size);

		conn->states=1;
	}else if(conn->states==1){
		int res=sendfile(conn->fd,filefd,NULL,file_buf.st_size);
		if(res==-1){
			printf("sendfile error :%d",errno);
		}

		conn->states=2;
	}else if( conn->states==2){
		conn->wlen = 0;
		memset(conn->wbuffer, 0, BUFFER_LENGTH);
		conn->states = 0;

	}
	

	close(filefd);
	return conn->wlen;;
}

int del_png(struct conn_item *conn){

	
	int filefd = open("wrk.png", O_RDONLY);

	
	struct stat file_buf;
	fstat(filefd, &file_buf);
	
	if(conn->states==0){

		conn->wlen = sprintf(conn->wbuffer, 
		"HTTP/1.1 200 OK\r\n"
		"Content-Type: image/png\r\n"
		"Accept-Ranges: bytes\r\n"
		"Content-Length: %ld\r\n"
		"Date: Tue, 30 Apr 2024 13:16:46 GMT\r\n\r\n", 
		file_buf.st_size);

		conn->states=1;
	}else if(conn->states==1){
		int res=sendfile(conn->fd,filefd,NULL,file_buf.st_size);
		if(res==-1){
			printf("sendfile error :%d",errno);
		}

		conn->states=2;
	}else if( conn->states==2){
		conn->wlen = 0;
		memset(conn->wbuffer, 0, BUFFER_LENGTH);
		conn->states = 0;

	}
	return conn->wlen;
}

int http_response(struct conn_item *conn,int opt){
     //printf("begin :response !!!!!!!!!!!");
	int count =0;
	
	switch (opt)
	{
		case 0:
			count =del_string(conn);
			break;
		case 1:
			count =del_html(conn);
			break;
		case 2:
			count =del_png(conn);
			break;
		default:
			printf("http_response : %d\n",opt);
			break;
	}



#if 0
	 if(rpack.str==1){
		

	 }else if(rpack.html==1){
		
	 }else{
		
	 }
#endif

 #if 0
	int filefd = open("index.html", O_RDONLY);

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

	conn->wlen = sprintf(conn->wbuffer, 
		"HTTP/1.1 200 OK\r\n"
		"Content-Type: text/html\r\n"
		"Accept-Ranges: bytes\r\n"
		"Content-Length: %ld\r\n"
		"Date: Tue, 30 Apr 2024 13:16:46 GMT\r\n\r\n", 
		file_buf.st_size);

	int count = read(filefd, conn->wbuffer + conn->wlen, BUFFER_LENGTH - conn->wlen);
	conn->wlen += count;

	close(filefd);

#endif
    return count;
}

       专属学习链接:https://xxetb.xetslk.com/s/4cnbDc

4.Http请求测试

        在进行http请求测试时需要注意的是,当收发的数据比较大的时候,一次用户态send()和recv()无法将所有需要的信息收发完成时,可以通过额外的引入一个状态机来进行状态的迁移控制,从而达到将所需的信息全部接收的效果,但需要仔细的思考状态在什么情况下需要改变,以达到想要的效果。由于对http的解析不太熟悉,本应根据网络的请求处理相应的业务,此篇的response回应请求部分的逻辑是预先已知请求,然后对应编写的回发,之后会再进行代码逻辑的补充,感兴趣的朋友也可相互探讨。

        测试结果如下:

        

5.代码qps的测试结果

6.reactor模型的总结

        可以发现代码网络端的更改很少,几乎只用考虑业务逻辑代码的编写,基本实现内核处理逻辑和业务处理的分离,充分的体现了reactor的优势。

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值