Webserver(5.4)项目整体

embedded/2024/11/13 9:31:43/

目录

  • http_conn.h
  • http_conn.cpp
  • locker.h
  • main.cpp
  • threadpool.h
  • 编译并创建线程池

http_conn.h

#ifndef HTTPCONNECTION_H
#define HTTPCONNEVTION_H
#include<sys/epoll.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/stat.h>
#include<sys/mman.h>
#include<stdarg.h>
#include<errno.h>
#include"locker.h"
#include<sys/uio.h>//解析HTTP请求报文
class http_conn{
public:static int m_epollfd; //所有的socket上的事件都被注册到同一个epoll对象中,所有设置为静态的static int m_user_count; //统计用户的数量http_conn(){}~http_conn(){}void process(); //处理客户端的请求void init(int sockfd,const sockaddr_in &addr);//初始化新接收的连接void close_conn(); //关闭连接bool read(); //非阻塞的读bool write(); //非阻塞的写private:int m_sockfd;//该HTTP连接的socketsockaddr_in m_address;//通信的socket地址
};#endif

http_conn.cpp

#include"http_conn.h"int http_conn::m_epollfd=-1; 
int http_conn::m_user_count=0;
//设置文件描述符非阻塞
int setnonblocking(int fd){int old_flag=fcntl(fd,F_GETFL);int new_flag=old_flag | O_NONBLOCK;fcntl(fd,F_SETFL,new_flag);
}//添加文件描述符到epoll中
//向epoll中添加需要监听的文件描述符
void addfd(int epollfd,int fd,bool one_shot){epoll_event event;event.data.fd=fd;event.events=EPOLLIN | EPOLLRDHUP;if(one_shot){event.events | EPOLLONESHOT;}epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);//设置文件描述符非阻塞setnonblocking(fd);}//从epoll中删除文件描述符
void removefd(int epollfd,int fd){epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);close(fd);
}//修改文件描述符,重置socket上EPOLLONESHOT事件,以确保下一次可读时,EPOLLIN事件能被触发
void modfd(int epollfd,int fd,int ev){epoll_event event;event.data.fd=fd;event.events=ev | EPOLLONESHOT | EPOLLRDHUP;epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
}//初始化新接收的连接
//前面要加http_conn::,要加上类名
void http_conn::init(int sockfd,const sockaddr_in &addr){m_sockfd=sockfd;m_address=addr;//设置一下端口复用,设置了才能加到epoll中int reuse=1;//只有为1这个值才表示复用setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));//添加到epoll对象中addfd(m_epollfd,sockfd,true);m_user_count++;//总用户数加1
}//关闭连接
void http_conn::close_conn(){if(m_sockfd!=-1){removefd(m_epollfd,m_sockfd);m_sockfd=-1;m_user_count--;//关闭一个连接,客户总数量减1}
}bool http_conn::read(){printf("一次性读完数据\n");return true;
}bool http_conn::write(){printf("一次性写完数据\n");return true;
}//由线程池中的工作线程调用,这是处理HTTP请求的入口函数
void http_conn::process(){//解析HTTP请求,做业务逻辑printf("parse request,create response");//生成响应
}

locker.h

#ifndef LOCKER_H
#define LOCKER_H#include<pthread.h>
#include<exception>
#include<semaphore.h>//线程同步机制封装类//互斥锁类
class locker{
public:locker(){if(pthread_mutex_init(&m_mutex,NULL)!=0){throw std::exception();}}~locker(){pthread_mutex_destroy(&m_mutex);}bool lock(){return pthread_mutex_lock(&m_mutex)==0;}bool unlock(){return pthread_mutex_unlock(&m_mutex)==0;}pthread_mutex_t * get(){return &m_mutex;}
private:pthread_mutex_t m_mutex;
};//条件变量类
class cond{
public:cond(){if(pthread_cond_init(&m_cond,NULL)!=0){throw std::exception();}}~cond(){pthread_cond_destroy(&m_cond);}bool wait(pthread_mutex_t * mutex){return pthread_cond_wait(&m_cond,mutex)==0;}bool timewait(pthread_mutex_t * mutex,struct timespec t){return pthread_cond_timedwait(&m_cond,mutex,&t)==0;}bool signal(){return pthread_cond_signal(&m_cond)==0;}bool broadcast(){return pthread_cond_broadcast(&m_cond)==0;}
private:pthread_cond_t m_cond;
};//信号量类
class sem{
public:sem(){if(sem_init(&m_sem,0,0)!=0){throw std::exception();}}sem(int num){if(sem_init(&m_sem,0,num)!=0){throw std::exception();}}~sem(){sem_destroy(&m_sem);}//等待信号量bool wait(){return sem_wait(&m_sem)==0;}//增加信号量bool post(){return sem_post(&m_sem)==0;}
private:sem_t m_sem;
};
#endif

main.cpp

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<error.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include<signal.h>
#include"locker.h"
#include"threadpool.h"
#include"http_conn.h"#define MAX_FD 65535 //最大文件描述符个数
//虽然最大有这么多,但是跑起来支持不了这么多并发
#define MAX_EVENT_NUMBER 10000 //监听的最大的事件数量,过少的话程序性能不行//添加信号捕捉
void addsig(int sig,void(handler)(int)){struct sigaction sa;memset(&sa,'\0',sizeof(sa));sa.sa_handler=handler;sigfillset(&sa.sa_mask);sigaction(sig,&sa,NULL);
}//添加文件描述符到epoll中
extern void addfd(int epollfd,int fd,bool one_shot);//从epoll中删除文件描述符
extern void removefd(int epollfd,int fd);//修改文件描述符
extern void modfd(int epollfd,int fd,int ev);int main(int argc,char* argv[]){if(argc<=1){printf("按照如下格式运行:%s port_number\n",basename(argv[0]));exit(-1);}//获取端口号int port = atoi(argv[1]);//对SIGPIE信号进行处理addsig(SIGPIPE,SIG_IGN);//创建线程池初始化信息threadpool<http_conn> * pool=NULL;try{pool=new threadpool<http_conn>;} catch(...){exit(-1);}//线程池都没创建好的话,直接退出//当前main函数是主线程,任务类是http_conn这个类//创建一个数组用于保存所有的客户端信息http_conn * users=new http_conn[ MAX_FD ];//写网络代码int listenfd=socket(PF_INET,SOCK_STREAM,0);//绑定前,设置端口复用int reuse=1;//只有为1这个值才表示复用setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));//绑定,绑定监听的套接字struct sockaddr_in address;address.sin_family=AF_INET;address.sin_addr.s_addr=INADDR_ANY;address.sin_port=htons(port);bind(listenfd,(struct sockaddr*)&address,sizeof(address));//监听listen(listenfd,5);//监听下面写epoll//创建epoll对象,事件数组,检测到之后把事件写到数组里面epoll_event events[MAX_EVENT_NUMBER];int epollfd=epoll_create(5);//将监听的文件描述符添加到epoll对象中addfd(epollfd,listenfd,false);http_conn::m_epollfd=epollfd;//主线程不断循环检测有无事件发生while(1){int num=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);//num表示检测到了几个事件if((num<0)&&(errno!=EINTR)){printf("epoll failure\n");break;}//循环遍历事件数组for(int i=0;i<num;i++){int sockfd=events[i].data.fd;if(sockfd==listenfd){//有客户端连接进来//需要连接客户端,用acceptstruct sockaddr_in client_address;socklen_t client_addrlen=sizeof(client_address);int connfd=accept(listenfd,(struct sockaddr*)&client_address,&client_addrlen);if(http_conn::m_user_count>=MAX_FD){//目前的连接数满了,//给客户端写一个信息:服务器内部正忙close(connfd);continue;}//将新的客户的数据初始化,放到数组中users[connfd].init(connfd,client_address);}else if(events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)){//对方异常断开或者错误等事件users[sockfd].close_conn();}else if(events[i].events &EPOLLIN){if(users[sockfd].read()){//一次性把所有数据都读完//交给工作线程去处理pool->append(users + sockfd);}else{users[sockfd].close_conn();}}else if(events[i].events&EPOLLOUT){if(!users[sockfd].write()){//一次性写完所有数据users[sockfd].close_conn();}}}}//先实现流程,再去实现具体的方法close(epollfd);close(listenfd);delete [] users;delete pool;return 0;
}

threadpool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H#include<pthread.h>
#include<list>
#include<exception>
#include<cstdio>
#include"locker.h"//线程池类,定义成模板类是为了代码的复用,模板参数T是任务类
template<typename T>
class threadpool{
public:threadpool(int thread_number=8,int max_requests=10000);~threadpool();bool append(T* request);private:static void* worker(void * arg);void run();private://线程的数量int m_thread_number;//线程池数组,大小为m_thread_numberpthread_t * m_threads;//请求队列中最多允许的,等待处理的请求数量int m_max_requests;//请求队列std::list< T*> m_workqueue;//互斥锁locker m_queuelocker;//信号量用来判断是否有任务需要处理sem m_queuestat;//是否结束线程bool m_stop;
};template<typename T>
threadpool<T>::threadpool(int thread_number,int max_requests) :m_thread_number(thread_number),m_max_requests(max_requests),m_stop(false),m_threads(NULL){if((thread_number <=0)||(max_requests<=0)){throw std::exception();}m_threads=new pthread_t[m_thread_number];if(!m_threads){throw std::exception();}//创建thread_number个线程,并将它们设置为线程脱离for(int i=0;i<thread_number;i++){printf("create the %dth thread\n",i);if(pthread_create(m_threads + i,NULL,worker,this)!=0){delete [] m_threads;throw std::exception();}if(pthread_detach(m_threads[i])){delete[] m_threads;throw std::exception();}}}template<typename T>
threadpool<T>::~threadpool(){delete[] m_threads;m_stop=true;
}template<typename T>
bool threadpool<T>::append(T *request){m_queuelocker.lock();if(m_workqueue.size()>m_max_requests){m_queuelocker.unlock();return false;}m_workqueue.push_back(request);m_queuelocker.unlock();m_queuestat.post();return true;
}template<typename T>
void* threadpool<T>::worker(void *arg){threadpool * pool=(threadpool *)arg;pool->run();return pool;
}template<typename T>
void threadpool<T>::run(){while(!m_stop){m_queuestat.wait();m_queuelocker.lock();if(m_workqueue.empty()){m_queuelocker.unlock();continue;} T* request=m_workqueue.front();m_workqueue.pop_front();m_queuelocker.unlock();if(!request){continue;}request->process();}
}
#endif

编译并创建线程池

编译所有cpp文件

g++ *.cpp -pthread

运行该程序并设置一个端口号10000
在这里插入图片描述
创建了8个线程
在这里插入图片描述

先记一下虚拟机的ip
192.168.227.129
去浏览器中用http访问一下
在这里插入图片描述
在这里插入图片描述


http://www.ppmy.cn/embedded/136456.html

相关文章

day07(单片机)中断

目录 中断 基本概念 中断的意义 中断处理过程(背过) 中断体系结构 NVIC 1&#xff09;管理中断事件&#xff08;清除、挂起&#xff09; 2&#xff09;支持中断向量化处理&#xff08;向量表&#xff09; 3&#xff09;支持中断嵌套 &#xff08;优先级&#xff09; E…

【AI抠图整合包及教程】Meta SAM2:引领图像和视频分割技术的新纪元

在人工智能的浪潮中&#xff0c;Meta公司再次以Segment Anything Model 2&#xff08;SAM 2&#xff09;引领了图像和视频分割技术的新纪元。SAM 2的发布不仅为计算机视觉领域的研究和发展注入了新的活力&#xff0c;还预示着这一技术将在多个行业中找到广泛的应用场景。这一创…

InfluxDB性能优化指南

1. 引言 1.1 InfluxDB的简介与发展背景 InfluxDB是一个开源的时间序列数据库&#xff08;TSDB&#xff09;&#xff0c;由InfluxData公司开发&#xff0c;专门用于处理高频率的数据写入和查询。其设计初衷是为物联网、应用程序监控、DevOps和实时分析等场景提供一个高效的存储…

HTML CSS H5C3样式语句汇总20241105

样式语句汇总&#xff1a; ----------------------------------样式设置------------------------------------ 盒子&#xff1a; border: 1px solid #000; 设置边框粗细、实线、颜色&#xff1b; margin: 0 auto; 盒子居中&#xff08;顺时针&#xff09;上0、右自适、下0、…

量化交易系统开发-实时行情自动化交易-Okex行情交易数据

19年创业做过一年的量化交易但没有成功&#xff0c;作为交易系统的开发人员积累了一些经验&#xff0c;最近想重新研究交易系统&#xff0c;一边整理一边写出来一些思考供大家参考&#xff0c;也希望跟做量化的朋友有更多的交流和合作。 接下来聊聊基于Okex交易所API获取行情数…

广义主从同步问题

广义主从同步问题&#xff08;Generalized Master-Slave Synchronization Problem&#xff09;通常涉及两个或多个动态系统&#xff0c;其中一个系统作为“主系统”&#xff08;master system&#xff09;&#xff0c;而其他系统作为“从系统”&#xff08;slave systems&#…

深入探讨Java中的File类

深入探讨Java中的File类 在Java编程中&#xff0c;文件和目录的操作是非常常见的需求&#xff0c;java.io.File 类则提供了对文件和目录进行操作的多种方法。虽然 File 类本质上并不提供对文件内容的读取和写入功能&#xff0c;但它是管理文件路径、判断文件状态、创建文件及目…

《深入浅出HTTPS​​​​​​​》读书笔记(7):安全的密码学Hash算法

密码学Hash算法除了常规Hash算法的特性&#xff0c;还应该具备下面三个特性。 1&#xff09;强抗碰撞性&#xff08;Collision Resistance&#xff09; 如果两个不相同的值能够得到同样的摘要值&#xff0c;表示产生了Hash碰撞。密码学中&#xff0c;Hash算法必须具备强抗碰撞性…