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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149398.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

.Net开源迁移框架FluentMigrator的使用。

在实际的开发过程中&#xff0c;经常会遇到数据库结构变动&#xff0c;比如新增表、删除表&#xff1b;已有的表新增字段&#xff0c;删除字段&#xff1b;修改字段属性等等。而且需要开发环境、测试环境和生产环境进行同步。如果使用的是EF&#xff0c;还是挺方便的。而非EF环…

PyTorch入门之【tensor】

目录 tensor的创建tensor的相关信息tensor的运算 tensor的创建 1.手动创建 import torch test1torch.tensor([1,2,3])#一维时为向量 test2torch.tensor([[1,2,3]])#二维时为矩阵 test3torch.tensor([[[1,2,3]]])#三维及以上统称为tensor print(test1) print(test2) print(tes…

【Linux】线程详解完结篇——信号量 + 线程池 + 单例模式 + 读写锁

线程详解第四篇 前言正式开始信号量引例信号量的本质信号量相关的四个核心接口生产消费者模型用环形队列实现生产者消费者模型基于环形队列的生产消费模型的原理代码演示单生产者单消费者多生产者多消费者 计数器的意义 线程池基本概念代码 单例模式STL,智能指针和线程安全STL中…

文心一言 VS 讯飞星火 VS chatgpt (106)-- 算法导论10.1 4题

四、用go语言&#xff0c;重写 ENQUEUE 和 DEQUEUE 的代码&#xff0c;使之能处理队列的下溢和上溢。 文心一言&#xff1a; 在 Go 语言中&#xff0c;我们可以使用切片来实现队列。下面是如何实现 ENQUEUE 和 DEQUEUE 操作&#xff0c;同时处理队列的下溢和上溢的情况&#…

git提交代码的流程

1.拉取代码 当你进入了一家公司就需要拉去公司的代码进行开发,此时你的项目小组长会给你个地址拉代码, git clone 公司项目的地址 此时如果不使用了这个方式拉去代码,拉去的是master分支上的代码,但是很多数的情况下&#xff0c;公司的项目可能会在其它的分支上,因此到公…

经典算法-----汉诺塔问题

前言 今天我们学习一个老经典的问题-----汉诺塔问题&#xff0c;可能在学习编程之前我们就听说过这个问题&#xff0c;那这里我们如何去通过编程的方式去解决这么一个问题呢&#xff1f;下面接着看。 汉诺塔问题 问题描述 这里是引用汉诺塔问题源自印度一个古老的传说&#x…

Python3数据科学包系列(一):数据分析实战

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 认识下数据科学中数据处理基础包: (1)NumPy 俗话说: 要学会跑需先…

<C++>类和对象-下

目录 一、构造函数的初始化 1. 构造函数体赋值 2. 初始化列表 2.1 概念 2.2 隐式类型转换式构造 2.3 explicit关键字 二、static静态成员 1. 概念 2. 特性 三、友元 1. 友元函数 2.友元类 四、内部类 1. 概念 五、匿名对象 1. const引用匿名对象 2. 匿名对象的隐式类型转换 总…

postgresql实现单主单从

实现步骤 1.主库创建一个有复制权限的用户 CREATE ROLE 用户名login # 有登录权限的角色即是用户replication #复制权限 encrypted password 密码;2.主库配置开放从库外部访问权限 修改 pg_hba.conf 文件 &#xff08;相当于开放防火墙&#xff09; # 类型 数据库 …

Swing程序设计(5)绝对布局,流布局

文章目录 前言一、布局管理器二、介绍 1.绝对布局2.流布局总结 前言 Swing窗体中&#xff0c;每一个组件都有大小和具体的位置。而在容器中摆放各种组件时&#xff0c;很难判断其组件的具体位置和大小。即一个完整的界面中&#xff0c;往往有多个组件&#xff0c;那么如何将这…

Unity如何实现TreeView

前言 最近有一个需求,需要实现一个TreeView的试图显示,开始我一直觉得这么通用的结构,肯定有现成的UI组件或者插件可以使用,结果,找了好久,都没有找到合适的插件,有两个效果差强人意。 最后在回家的路上突然灵光一闪,想到了一种简单的实现方式,什么插件都不用,仅使用…

基于虚拟同步发电机控制的双机并联Simulink仿真模型

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

1024 科学计数法

一.问题&#xff1a; 科学计数法是科学家用来表示很大或很小的数字的一种方便的方法&#xff0c;其满足正则表达式 [-][1-9].[0-9]E[-][0-9]&#xff0c;即数字的整数部分只有 1 位&#xff0c;小数部分至少有 1 位&#xff0c;该数字及其指数部分的正负号即使对正数也必定明确…

kafka集群工作机制

一、kafka在zookeeper上的元数据解释 kafka中的broker要选举Controller角色来管理整个kafka集群中的分区和副本状态。一个Topic下多个partition要选举Leader角色和客户端进行交互数据 Zookeeper客户端工具&#xff1a; prettyZoo。 下载地址&#xff1a;https://github.com/vr…

2023年R1快开门式压力容器操作证模拟考试题库及R1快开门式压力容器操作理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年R1快开门式压力容器操作证模拟考试题库及R1快开门式压力容器操作理论考试试题是由安全生产模拟考试一点通提供&#xff0c;R1快开门式压力容器操作证模拟考试题库是根据R1快开门式压力容器操作最新版教材&#…

润滑油泵控制(博途SCL源代码)

有关博途PLC定时器的各种使用方法请参考下面文章链接: 博途PLC IEC定时器编程应用(SCL语言)_博图 定时器-CSDN博客博途PLC定时器支持数据类型TIME 类型 ,写法支持T#2M10S 、T#10S等,时基是MS所以如果设置1M用 DINT数据类型就是60000,大部分HMI上数据类型很多不支持IEC的…

buuctf-[GXYCTF2019]禁止套娃 git泄露,无参数rce

用dirsearch扫一下&#xff0c;看到flag.php 访问一下没啥东西&#xff0c;使用githack python2 GitHack.py http://8996e81f-a75c-4180-b0ad-226d97ba61b2.node4.buuoj.cn/.git/查看index.php <?php include "flag.php"; echo "flag在哪里呢&#xff1f;…

【iptables 实战】9 docker网络原理分析

在开始本章阅读之前&#xff0c;需要提前了解以下的知识 阅读本节需要一些docker的基础知识&#xff0c;最好是在linux上安装好docker环境。提前掌握iptables的基础知识&#xff0c;前文参考【iptables 实战】 一、docker网络模型 docker网络模型如下图所示 说明&#xff1…

【算法|动态规划No.9】leetcodeLCR 091. 粉刷房子

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…