EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理

本文主要是介绍EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前写了一个httpserver的问价下载服务器    如果有多个客户端请求过来只能串行处理必须得等当前的操作完成之后才会处理   

另外还存在 文件大的时候 会出错 处理不了  原因就是 sendfile是在一个while循环中处理的  

当调用send失败返回-1之后 就  结束了   而一般来讲  send的时候发送的数据超过内核中的send buffer的大小的时候  就会  失败了  

这个时候 必须 要保存下来当前文件的已发送的字节数 以及当前文件的偏移指针 等下一次 EPOLLOUT事件的时候再次 发送给客户端  

目前已经实现了这个功能 采用的是单线程版本的reactor模式  

支持 多个客户端同时下载文件 

还存在bug 但是  功能是有了  

#include <stdio.h>
#include <stdlib.h>#include <signal.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <unordered_map>
#include <memory>
#include <vector>#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/sendfile.h>#include <dirent.h>typedef int (*READ_CB)(void *user_data);
typedef int (*WRITE_CB)(void *user_data);
typedef int (*ACCEPT_CB)(int epoll_fd,int fd,void *user_data);#define READ_ONETIME	100#define MAX_SESSIONS	1024
typedef struct
{int fd;int file_fd = -1;char write_buffer[1024];char read_buffer[1024];	int write_offset;int read_offset;int send_file_read_len = 0;char writeable;char is_dir;char head_has_send = 0;char file_path[512]={0};int file_size = 0;READ_CB read_cb;WRITE_CB write_cb;ACCEPT_CB accept_cb;
}Session;typedef struct 
{int epoll_fd;int server_fd;int count;Session sessions[MAX_SESSIONS];}Reactor;int create_socket(bool is_tcp,bool block_mode,const char *led_ip,int port)
{#define LISTEN_BACKLOG 10int socket_fd ;const char *server_ip = led_ip;struct sockaddr_in server_addr;if(is_tcp){if(block_mode){socket_fd = socket(AF_INET,SOCK_STREAM,0);	}else{socket_fd = socket(AF_INET,SOCK_STREAM|SOCK_NONBLOCK,0);}}else{if(block_mode){socket_fd = socket(AF_INET,SOCK_DGRAM,0);	}else{socket_fd = socket(AF_INET,SOCK_DGRAM|SOCK_NONBLOCK,0);}}int opt = 1;if (socket_fd == -1) {printf("Create socket error\n");goto ERROR;}setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);inet_pton(AF_INET,server_ip,&server_addr.sin_addr);if (bind(socket_fd, (struct sockaddr *) &server_addr,sizeof(server_addr)) == -1){printf("Bind error\n");goto ERROR;}if (listen(socket_fd, LISTEN_BACKLOG) == -1){printf("listen error\n");goto ERROR;}return socket_fd;ERROR:if(socket_fd>0){close(socket_fd);}return -1;
}void set_nonblock(int fd)
{int opts=fcntl(fd, F_GETFL);	if(opts<0)	{	fprintf(stderr, "fcntl(sock,GETFL)\n");  return ;} opts = opts|O_NONBLOCK;  if(fcntl(fd,F_SETFL,opts)<0)	{	fprintf(stderr, "fcntl(sock,SETFL,opts)\n");  return; }	}int reactor_init(Reactor &rt,ACCEPT_CB accept_cb,READ_CB read_cb,WRITE_CB write_cb)
{rt.epoll_fd = epoll_create(10); if(rt.epoll_fd == -1){perror("epoll_create failed");return -1;}rt.server_fd = create_socket(true, true, "0,0,0,0", 1234);if(rt.server_fd == -1){perror("create_socket failed");close(rt.epoll_fd);return -1;}struct epoll_event event;event.data.fd = rt.server_fd;event.events = EPOLLIN|EPOLLET|EPOLLOUT;int ret = epoll_ctl(rt.epoll_fd,EPOLL_CTL_ADD ,rt.server_fd,&event);if(ret == -1){perror("epoll_ctl failed");close(rt.epoll_fd);close(rt.server_fd);		return -1;}for(int i = 0;i<MAX_SESSIONS;i++){rt.sessions[i].accept_cb = accept_cb;rt.sessions[i].read_cb = read_cb;		rt.sessions[i].write_cb = write_cb;				}rt.count = 0;printf("Reactor init success epollfd = %d serverfd = %d\n",rt.epoll_fd,rt.server_fd);return 0;
}int reactor_run(Reactor &rt)
{struct epoll_event events[100];while(true)	{int ready_count = epoll_wait(rt.epoll_fd, events, 100, -1);//printf("ready_count = %d\n",ready_count);for(int i = 0;i<ready_count;i++){int index = events[i].data.fd;//printf("index = %d epollfd = %d events[i].data.fd = %d events=%08X\n",index,rt.epoll_fd,events[i].data.fd,events[i].events);			Session * session = &rt.sessions[index];if(events[i].data.fd == rt.server_fd){printf("index = %d epollfd = %d cfd = %d\n",index,rt.epoll_fd,events[i].data.fd);session->accept_cb(rt.epoll_fd,events[i].data.fd,&rt);}else{if(events[i].events & EPOLLIN){session->read_cb(session);}if(events[i].events & EPOLLOUT){session->write_cb(session);}				}}}
}int reactor_deinit(Reactor &rt)
{if(rt.epoll_fd >0){close(rt.epoll_fd);}return 0;
}int Accept_cb(int epoll_fd,int fd,void *user_data)
{if(fd > 0 && epoll_fd >0){int cfd = accept(fd,NULL,NULL);if(cfd == -1){perror("accept failed");return -1;}set_nonblock(cfd);printf("Accept_cb epollfd = %d cfd = %d\n",epoll_fd,cfd);struct epoll_event ev = {0};ev.data.fd = cfd;ev.events = EPOLLIN|EPOLLOUT|EPOLLET;int ret = epoll_ctl(epoll_fd,EPOLL_CTL_ADD,cfd,&ev);if(ret == -1){perror("epoll_ctrl failed");return -1;}Reactor *reactor = (Reactor*)user_data;reactor->sessions[cfd].fd = cfd;//session->fd = cfd;return 0;}return -1;
}void http_request(Session *session)
{char method[12]={0},path[512]={0},protocol[20]={0},headers[512]={0};printf("buf len[%d] content[%s]\n",session->read_offset,session->read_buffer);char *p = strstr(session->read_buffer,"\r\n\r\n");int ret = sscanf(session->read_buffer,"%[^ ] %[^ ] %[^ \r\n]%[^\r\n]",method,path,protocol,headers);printf("sscanf ret is %d headers is %s\n",ret,headers);if(ret !=3){printf("Wait a whole http header\n");session->writeable = 0;return ;}else{printf("This is a whole http packet\n");}session->writeable = 1;session->read_offset = 0;if(strcasecmp(method,"get") == 0){if(strcmp(path,"/") == 0){	strcpy(session->file_path ,"./");}else{strcpy(session->file_path ,path+1);}struct stat st;int ret = stat(session->file_path,&st);if(ret == -1){printf("file doest not exist\n");//SendHead(event,404,"Not found",GetFileType(".html"),-1);//SendFile(event,"404.html");session->is_dir = -1;return ;}if(S_ISDIR(st.st_mode)){printf("Directory\n");//SendHead(event,200,"OK",GetFileType(".html"),-1);//SendDir(event,file);session->is_dir = 1;}else{printf("File\n");session->file_size = st.st_size;//SendHead(event,200,"OK",GetFileType(file),st.st_size);//SendFile(event,file);session->is_dir = 0;}}}#define BURSIZE 1024
int hex2dec(char c)
{if ('0' <= c && c <= '9') {return c - '0';} else if ('a' <= c && c <= 'f') {return c - 'a' + 10;} else if ('A' <= c && c <= 'F') {return c - 'A' + 10;} else {return -1;}
}char dec2hex(short int c)
{if (0 <= c && c <= 9) {return c + '0';} else if (10 <= c && c <= 15) {return c + 'A' - 10;} else {return -1;}
}/** 编码一个url*/
void urlencode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (('0' <= c && c <= '9') ||('a' <= c && c <= 'z') ||('A' <= c && c <= 'Z') || c == '/' || c == '.') {res[res_len++] = c;} else {int j = (short int)c;if (j < 0)j += 256;int i1, i0;i1 = j / 16;i0 = j - i1 * 16;res[res_len++] = '%';res[res_len++] = dec2hex(i1);res[res_len++] = dec2hex(i0);}}res[res_len] = '\0';strcpy(url, res);
}/** 解码url*/
void urldecode(char url[])
{int i = 0;int len = strlen(url);int res_len = 0;char res[BURSIZE];for (i = 0; i < len; ++i) {char c = url[i];if (c != '%') {res[res_len++] = c;} else {char c1 = url[++i];char c0 = url[++i];int num = 0;num = hex2dec(c1) * 16 + hex2dec(c0);res[res_len++] = num;}}res[res_len] = '\0';strcpy(url, res);
}const char *GetFileType(const char *filename)
{const char *dot = strrchr(filename,'.');if(dot == NULL){return "text/plain; charset=utf-8";}if(strcmp(dot,".jpg") == 0 ||strcmp(dot,".jpeg") == 0){return "image/jpg";}if(strcmp(dot,".html") == 0 ||strcmp(dot,".htm") == 0){return "text/html; charset=utf-8";}    if(strcmp(dot,".png") == 0){return "image/png";}    if(strcmp(dot,".bmp") == 0){return "image/bmp";}        if(strcmp(dot,".gif") == 0){return "image/gif";}            if(strcmp(dot,".css") == 0){return "text/css";}           if(strcmp(dot,".mp3") == 0){return "audio/mpeg";}               return "text/plain; charset=utf-8";
}int SendHead(int cfd,int status ,const char *desc,const char *type,int size)
{char buf[4096] = {0};sprintf(buf,"http/1.1 %d %s\r\n",status,desc);sprintf(buf+strlen(buf),"content-type: %s\r\n",type);sprintf(buf+strlen(buf),"content-length: %d\r\n\r\n",size);    printf("SendHead buf[%s]\n",buf);return send(cfd,buf,strlen(buf),0);
}int SendDir(Session *session,const char *dirname)
{char buf[4096] = {0};sprintf(buf,"<html><head><title>%s</title></head><body><table>",dirname);printf("SendDir dirname=[%s]\n",dirname);struct dirent **namelist;int count = scandir(dirname,&namelist,NULL,alphasort);printf("SendDir count=[%d]\n",count);for(int i = 0;i< count;i++){char *name = namelist[i]->d_name;struct stat st;char sub_path[1024]={0};sprintf(sub_path,"%s/%s",dirname,name);stat(sub_path,&st);if(S_ISDIR(st.st_mode)){sprintf(buf+strlen(buf),"<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}else{sprintf(buf+strlen(buf),"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",name,name,st.st_size);}//printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(session->fd,buf,strlen(buf),0);memset(buf,0,sizeof(buf));free(namelist[i]);}sprintf(buf,"</table></body></html>");//printf("cfd:%d Sendbuf[%s]\n",cfd,buf);send(session->fd,buf,strlen(buf),0);free(namelist);return 0;
}int SendFile(Session *session,const char* filename)
{if(session->file_fd == -1){session->file_fd = open(filename,O_RDONLY); 		}if(session->file_fd >0){#if 1while(1){char buf[1024];int len = read(session->file_fd,buf,sizeof (buf));if(len >0){session->send_file_read_len+=len;int ret = send(session->fd,buf,len,0);if(ret >0){session->write_offset += ret;//printf("This time send [%d] total send [%d] bytes\n",ret,session->write_offset);}else if(ret ==0){printf("Send file return 0 close socket this time len = %d total len = %d \n",len,session->send_file_read_len);close(session->file_fd);				close(session->fd);}else{int seek_ret = lseek(session->file_fd,session->write_offset,SEEK_SET);//printf("Seekret = %d session->writeoffset = %d\n",seek_ret,session->write_offset);if(seek_ret == -1){perror("lseek failed");}session->send_file_read_len-=len;//printf("Send file return -1 wait next send this time len = %d total len = %d\n",len,session->send_file_read_len);return -1;}}else if(len == 0){printf("Read file end this time len = %d total len = %d\n",len,session->send_file_read_len);close(session->file_fd);				close(session->fd);session->write_offset = 0;session->send_file_read_len = 0;session->fd = 0;session->file_fd = -1;session->writeable = 0;return 0;break;}else{close(session->file_fd);				close(session->fd);perror("read error");}}#elseoff_t offset = 0;int file_size = lseek(fd,0,SEEK_END);lseek(fd,0,SEEK_SET);while(offset <file_size){int send_len = sendfile(cfd,fd,&offset,file_size-offset);if(send_len == -1){if(errno == EAGAIN){//perror("sendfile no data send");}else{perror("sendfile ret -1");}}else{printf("Send len:%d\n",send_len);}}#endif}else{perror("open file failed");}//close(fd);return 0;
}void http_response(Session *session)
{//printf("session->writeable = %d\n",session->writeable);if(session->writeable == 0){printf("Not writable\n");return ;}if(session->is_dir == -1){if(session->head_has_send == 0){SendHead(session->fd,404,"Not found",GetFileType(".html"),-1);			session->head_has_send = 1;}SendFile(session,"404.html");	session->writeable = 0;}else if(session->is_dir == 1){if(session->head_has_send == 0){SendHead(session->fd,200,"OK",GetFileType(".html"),-1);			session->head_has_send = 1;}SendDir(session,session->file_path);			}else if(session->is_dir == 0){if(session->head_has_send == 0){SendHead(session->fd,200,"OK",GetFileType(session->file_path),session->file_size);			session->head_has_send = 1;}SendFile(session,session->file_path);			}}int Read_cb(void *user_data)
{int nread,offset = 0;  if(user_data == NULL) return -1;Session *sesion = (Session *)(user_data);printf("Enter readcb1111 sesion->fd = %d\n",sesion->fd);	if(sesion){while ((nread = read(sesion->fd, sesion->read_buffer+sesion->read_offset, 1024-1)) > 0) {  sesion->read_offset += nread; http_request(sesion);}  printf("nread = %d\n",nread);if (nread == -1 && errno != EAGAIN) {  perror("read error");  } //conn->recv_size = offset;}return 0;
}int Write_cb(void *user_data)
{if(user_data == NULL) return -1;Session *session = (Session *)(user_data);http_response(session);return 0;
}int main(int argc ,char *argv[])
{printf("Reactor\n");signal(SIGPIPE, SIG_IGN);Reactor reactor;reactor_init(reactor,Accept_cb,Read_cb,Write_cb);reactor_run(reactor);reactor_deinit(reactor);return 0;
}

这篇关于EPOLL单线程版本 基于reactor 的 httpserver文件下载 支持多个客户端同时处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/171765

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

2. 下载rknn-toolkit2项目

官网链接: https://github.com/airockchip/rknn-toolkit2 安装好git:[[1. Git的安装]] 下载项目: git clone https://github.com/airockchip/rknn-toolkit2.git 或者直接去github下载压缩文件,解压即可。

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

前端form表单+ifarme方式实现大文件下载

// main.jsimport Vue from 'vue';import App from './App.vue';import { downloadTokenFile } from '@/path/to/your/function'; // 替换为您的函数路径// 将 downloadTokenFile 添加到 Vue 原型上Vue.prototype.$downloadTokenF

C++ I/O多路复用 select / poll / epoll

I/O多路复用:在网络I/O中,用 1个或1组线程 管理 多个连接描述符。             如果有至少一个描述符准备就绪,就处理对应的事件             如果没有,就会被阻塞,让出CPU给其他应用程序运行,直到有准备就绪的描述符 或 超时