服务器整体运行流程

  • 服务器介绍
  • 服务器端整体运行流程
    • webserver的初始化
    • 日志、数据库、线程池和触发模式
    • 主线程监听连接
    • 主线程处理监控文件描述符上的事件

服务器介绍

本项目大部分参考社长的TinyWebServer。首先认识一下什么是服务器。

服务器就是一个服务器软件(程序),其主要功能是通过HTTP协议与客户端(通常是浏览器browser)进行通信,并对其请求做出HTTP响应,返回客户端所请求的内容(文件,网页等)。

通常用户使用Web浏览器与相应服务器进行通信。在浏览器中键入 “域名”“IP地址:端口号”,浏览器则先将你的域名解析成相应的IP地址或者直接根据你的IP地址向对应的Web服务器发送一个HTTP请求。这一过程首先要通过TCP协议的三次握手建立与目标Web服务器的连接,然后HTTP协议生成针对目标Web服务器的HTTP请求报文,通过TCP、IP等协议发送到目标Web服务器上。

服务器端整体运行流程

webserver的初始化

首先我们在服务器端通过mysql建立一个名叫“yourdb”的数据库,登录数据库的用户名和密码默认为root,123456。并且在“yourdb”中建立一张包含userpassword两个字段的名叫user的表。

当我们在命令行运行服务器程序,我们可以自定义设置端口号,线程池数量,日志写入方式,反应堆模型等等。
因为我们程序将运行config.parse_arg()进行命令行解析,获取我们的自定义设置,当然也可以使用默认设置。

void Config::parse_arg(int argc, char*argv[]){int opt;//getopt()方法是用来分析命令行参数//argc:通常由 main 函数直接传入,表示参数的数量//argv:通常也由 main 函数直接传入,表示参数的字符串变量数组//*str用于参数的解析。例如 “abc:”,其中 -a,-b 就表示两个普通选项,//-c 表示一个必须有参数的选项,因为它后面有一个冒号。//全局变量optarg:如果某个选项有参数,这包含当前选项的参数字符串const char *str = "p:l:m:o:s:t:c:a:";while ((opt = getopt(argc, argv, str)) != -1){switch (opt){case 'p':{PORT = atoi(optarg);break;}case 'l':{LOGWrite = atoi(optarg);break;}case 'm':{TRIGMode = atoi(optarg);break;}case 'o':{OPT_LINGER = atoi(optarg);break;}case 's':{sql_num = atoi(optarg);break;}case 't':{thread_num = atoi(optarg);break;}case 'c':{close_log = atoi(optarg);break;}case 'a':{actor_model = atoi(optarg);break;}default:break;}}
}

接着调用默认构造函数初始化webserver对象,即创建HTTP对象数组和资源文件夹路径初始化。最后就可以运行webserver.init()进行初始化。

void WebServer::init(int port, string user, string passWord, string databaseName, int log_write, int opt_linger, int trigmode, int sql_num, int thread_num, int close_log, int actor_model)
{//初始端口号m_port = port;//初始登录名m_user = user;//初始登录密码m_passWord = passWord;//初始化数据库名m_databaseName = databaseName;//初始化数据库连接池数量m_sql_num = sql_num;//初始化线程池内的线程数量m_thread_num = thread_num;//初始化日志写入方式m_log_write = log_write;//初始优雅关闭连接m_OPT_LINGER = opt_linger;//初始化触发组合模式m_TRIGMode = trigmode;//关闭日志,默认0不关闭m_close_log = close_log;//初始化并发模式,默认proactorm_actormodel = actor_model;
}

日志、数据库、线程池和触发模式

日志和数据库均只有一个实例对象,均通过局部静态成员变量的懒汉模式实现单例模式。优点:延迟实例化,节约资源;线程安全;性能提高;不存在内存泄漏。

日志的初始化需要设置日志文件名,日志缓冲区大小,日志最大行数。如果是异步方式写入日志,我们还需要设置阻塞队列的大小,本项目借鉴生产者消费者模型,采用循环数组结构实现阻塞队列。

为什么要创建连接池?

从一般流程中可以看出,若系统需要频繁访问数据库,则需要频繁创建和断开数据库连接,而创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。

在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,更加安全可靠。将数据库连接的获取与释放通过RAII机制封装(也称为**“资源获取就是初始化”**,是c++等编程语言常用的管理资源、避免内存泄露的方法),避免手动释放。

数据库连接池内部通过链表list存储mysql连接。并且使用信号量机制进行同步。

整个项目采用半同步\半反应堆的并发模式工作,线程池的内部构造类似数据库连接池,同样采样链表将HTTP对象存储起来形成请求队列,同时使用信号量机制和互斥锁进行同步。初始化线程池需要设置线程池数量(默认为8)、并发模式和能接受的最大请求队列长度

需要设置用于监听的套接字(listenfd)和用于通信的套接字(connfd)的触发模式,默认是同步I/O模拟的proactor模式。

主线程监听连接

使用socket()创建一个用于监听连接的套接字,并绑定默认的网卡和默认的端口(9006),我设置了端口复用,能复用处于TIME_WAIT的socket。

创建epoll实例,将listenfd加入epoll树并注册其读事件。

创建一对套接字进行管道通信,管道写端将定时信号发送给管道读端。管道读端的可读事件加入epoll实例中,即统一事件源

统一事件源,是指将信号事件与其他事件一样被处理。

最后设置时钟信号终止信号的的处理动作(即管道写端将信号发送给管道读端),至此就完成了准备工作。

主线程处理监控文件描述符上的事件

void WebServer::eventLoop()
{bool timeout = false;bool stop_server = false;while (!stop_server){//等待所监控文件描述符上有事件的产生//检测发生事件的文件描述符int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);if (number < 0 && errno != EINTR) //被中断的系统调用返回EINTR{LOG_ERROR("%s", "epoll failure");break;}//轮询文件描述符for (int i = 0; i < number; i++){int sockfd = events[i].data.fd;//处理新到的客户连接if (sockfd == m_listenfd){bool flag = dealclinetdata();if (false == flag)continue;}                         //对端断开连接|套接字意外关闭?|异常连接else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)){//服务器端关闭连接,移除对应的定时器util_timer *timer = users_timer[sockfd].timer;deal_timer(timer, sockfd);}//处理信号//管道读端对应的文件描述符发生读事件else if ((sockfd == m_pipefd[0]) && (events[i].events & EPOLLIN)){bool flag = dealwithsignal(timeout, stop_server);if (false == flag)LOG_ERROR("%s", "dealclientdata failure");}//处理客户端发送数据的读事件else if (events[i].events & EPOLLIN){dealwithread(sockfd);}//处理向客户端发送数据的写事件else if (events[i].events & EPOLLOUT){dealwithwrite(sockfd);}}if (timeout){//处理定时任务,并重新定时以不断触发SIGALRM信号utils.timer_handler();LOG_INFO("%s", "timer tick");timeout = false;}}
}

从以上**dealwithread()dealwithwrite()**内部实现可以看出两种并发模式( reactorproactor )的区别。简单来说:

reactor模式中,主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话立即通知工作线程(逻辑单元 ),读写数据、接受新连接及处理客户请求均在工作线程中完成。通常由同步I/O实现。

proactor模式中,主线程和内核负责处理读写数据、接受新连接等I/O操作,工作线程仅负责业务逻辑,如处理客户请求。

同步I/O模拟proactor模式的工作流程如下(epoll_wait为例):

  1. 主线程往epoll内核事件表注册socket上的读就绪事件。
  2. 主线程调用epoll_wait等待socket上有数据可读。
  3. 当socket上有数据可读,epoll_wait通知主线程,主线程从socket循环读取数据,直到没有更多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列
  4. 睡眠在请求队列上某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件。
  5. 主线程调用epoll_wait等待socket可写。
  6. 当socket上有数据可写,epoll_wait通知主线程。主线程往socket上写入服务器处理客户请求的结果。

C++Web服务器(一):服务器整体运行流程相关推荐

  1. SSM框架中MVC各层的作用以及运行流程

    这篇博文主要介绍的是SSM(Spring.SpringMVC.Mybatis)框架中,MVC各层的作用以及各层之间的交互和框架整体运行流程. 一.MVC各层级间的作用及关系 表现层(springMVC ...

  2. Spark SQL之queryExecution运行流程解析Logical Plan(三)

    1.整体运行流程 使用下列代码对SparkSQL流程进行分析,让大家明白LogicalPlan的几种状态,理解SparkSQL整体执行流程 // sc is an existing SparkCont ...

  3. Go语言实战 : API服务器 (2) 运行流程

    1.API服务器的总流程 分为两步: 启动API服务器 API服务器对HTTP请求进行处理 2.API服务器启动流程 解析配置文件,利用配置文件完成对服务器的初始化配置 初始化logger,开启日志记 ...

  4. 安防4G摄像头视频流媒体服务器EasyNVR关于视频集成自我展示web端嵌入视频广场的流程

    背景分析 随着互联网基础设施建设的发展,4G/5G/NB-IoT各种网络技术的大规模商用,视频随时随地可看.可控的诉求越来越多,互联网思维.架构和技术引入进传统监控行业里,成为新形势下全终端监控的基础 ...

  5. uban服务器系统,Web服务器-并发服务器-Epoll(3.4.5)

    @ 1.介绍 epoll是一种解决方案,nginx就是用的这个 中心思想:不要再使用多进程,多线程了,使用单进程,单线程去实现并发 在上面博客实现的代码中使用过的轮询去查看套接字有没有数据,而epol ...

  6. 用Scala实现简单的Web和API服务器

    [CSDN 编者按]大家都知道Web和API服务器在互联网中的重要性,在计算机网络方面提供了最基本的界面.本文主要介绍了怎样利用Scala实现实时聊天网站和API服务器,通过本篇文章,你定将受益匪浅. ...

  7. 云服务器php文件怎么运行,云服务器php文件怎么运行环境

    云服务器php文件怎么运行环境 内容精选 换一换 镜像是云耀云服务器运行环境的模板,模板中包含了特定的操作系统和运行环境,有时也额外包括了一些预装的应用软件.通过镜像可以部署特定的软件环境,也可以将云 ...

  8. 百度云服务器nginx搭建部署全流程

    今天百度云界面变了 我前几天买的时候 几十块钱一年 我买的是LS轻量服务器 云服务器或者其他服务器流程基本也都一样 我感觉 买完服务器以后点击右上角管理控制台 进入如下页面 悬停蓝色折叠菜单块-> ...

  9. 服务器CGI模式的运行机制

    CGI概括: 定义 通用网关接口(Common Gateway Interface)是HTTP服务器与你的或其它机器上的程序进行"交谈"的一种工具,其程序须运行在网络服务器上. 功 ...

最新文章

  1. C++ 中隐藏DOS调用的命令行窗口
  2. mysql lbs 附近的人_一口气说出 4种 LBS “附近的人” 实现方式,面试官笑了
  3. PVN3D: 基于Deep Point-wise 3D关键点投票的6D姿态估计网络(香港科技大学提出)
  4. 用Python进行机器学习(附代码、学习资源)
  5. 一个普通80后的IT Pro去溜冰的感慨
  6. iOS开发之第三方框架Masonry
  7. 在res/xml中的文件
  8. SQL经典面试题及答案
  9. 有趣的php实例,8个必备的PHP功能实例代码
  10. C++map容器-排序
  11. 使用google翻译api
  12. 如何在VMware Workstation上安装Windows Home Server Beta“ Vail”
  13. c语言许多名字随机抽取名字,怎么用ppt实现一个随机抽取名字的功能
  14. Python数据分析与展示-图像的手绘效果
  15. 量子计算机原理 纠缠,白话量子计算机原理【前面的那个有错误,重新理清了一下思路】...
  16. 使用 SpringMail +163 邮箱 发送邮件的方法
  17. 【已解决】 ‘gbk‘ codec can‘t decode byte 0x93 in position 3136: illegal multibyte sequence
  18. go语言webSocket框架——gorilla
  19. 2021-11-06
  20. 维修服务器bga是什么,服务器主板芯片坏了有机器能拆除焊接BGA吗?

热门文章

  1. 学计算机投影仪定义,一种计算机教学用投影仪射头的制作方法
  2. java取得对象占用的内存大小
  3. MongoDB数据库的安装及使用教程
  4. 抓包工具--Fiddler
  5. c3po数据库连接池简单配置
  6. spreadtrum 6600L 开机init流程
  7. 「前端进阶」JS中的内存管理
  8. AbpVnext 微服务 内部网关服务通讯 动态API客户端
  9. 纪念一下毛星云这位年轻的先生
  10. python中map函数返回值类型_Python学习第42课-map()函数