Skip to content

Create a light weight web server in Linux using c/c++(利用c/c++在linux下建立一个轻量级的web服务器)

Notifications You must be signed in to change notification settings

wangt1xiuyi/my_server_http_web

Repository files navigation

erver_http_web

using c/c++ to build a program for http server in Linux

2018.5.21

一、基于Linux下多线程的web服务器

1.使用语言:c/c++

2.项目介绍:

1)采用典型的c/s服务器框架结构

2)采用非阻塞I/O模型,同时通过erno值判断读取情况

3)采用Reactor事件处理模式:

i)主线程只负责监听文件描述上是否有事件发生,有的话就立即将该事件通知工作线程。除此之外,主线程不做任何其他实质性的工作。工作流程如下(epoll为例):

ii)主线程往epoll内核事件表注册socket上的读就绪事件。

iii)主线程调用epoll_wait等待socket上有数据可读

iv)当socket上有数据可读,epoll_wait通知主线程。主线程则将socket可读事件放入请求队列。

v)睡眠在请求队列上某个工作线程被唤醒,它从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该socket上的写就绪事件

vi)主线程调用epoll_wait等待socket可写。

vii)当socket上有数据可写,epoll_wait通知主线程。主线程则将socket可写事件放入请求队列。

viii)睡眠在请求队列上某个工作线程被唤醒,它从socket写入服务器处理客户请求的结果。

4)由(3)可知采用了主流的epoll,其中为了提高效率,采用了ET工作模式,同时由于一个socket事件还是可能被触发多次(并发程序中,多个线程处理同一个socket),对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个可读、可写或异常事件,且只触发一次,除非使用了epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。

5)为了节约系统开销,采用线程池,因此封装了一个线程池类,主要采用的是半同步/半反应堆的并发模式,因为使用一个工作队列可以完全解除了主线程和工作线程的耦合关系:主线程往工作队列中插入任务,工作线程通过竞争来获取并执行它。(这种方式必须保证请求无状态,因为同一个连接的请求可能会被不同工作线程处理)

6)同时基于oop的设计思想,封装了一个锁类(包含互斥锁、信号量及条件变量)解决线程之间的竞争问题

7)采用状态机的设计思想,按状态转移的方式设计了一个http的连接处理类

3.效果

目前采用了一个通用的基于epoll的多进程程序,创建指定的进程数,模拟指定数的客户端,然后同时连接上所写服务器,不断进行数据的交换,测试其效果,目前已测可以实现上万的并发连接数据交换。

二、 针对非活跃连接的处理,由于非活跃连接占用了连接资源,严重影响服务器的性能,因此接下来设计一个服务器定时器,处理这种非活跃连接,释放连接资源。

2018.5.22

实现处理非活跃连接方法:

一是利用Linux在内核中提供了对连接是否处于活动状态的定期检查机制,可以通过socket选项KEEPALIVE来激活它。不过这种方式将使得应用程序对连接的管理变得复杂。

因此采用第二种方法,基于定时器的心跳检测法。利用alarm函数定时产生SIGALRM信号的原理设计定时器,常用的定时器主要有基于时间上升序列定时器、时间轮以及最小时间堆,后两者更高效,因为添加定时器时间复杂度更小。

但是为了方便设计,我采用了基于时间上升序列的定时器,其他两种也会给出,拱参考。

基于时间上升序列的方法就是利用双向链表的设计思想,每个节点保存有定时的相关信息,安照时间长短由短到长排列,通过心跳检测函数定期检测到期时间的定时,并调用相关的处理函数进行处理。

为了方便统一管理,采用了统一事件源的事件处理方式;信号处理函数和程序的主循环是两条不同的执行路线。因此,信号处理函数需要尽可能地执行完毕,以确保该信号不被屏蔽太久。一种典型的解决方案是:把信号的主要处理逻辑放到程序的主循环中,当信号处理函数被触发时,它只是简单地通知主循环程序接收到信号,并把信息值传递给主循环,主循环在根据接收到地信号值执行目标信号对应的逻辑代码。信号处理函数通常使用管道来将信号“传递”给主循环:信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出该信号值。利用I/O复用检测。

以上,就是关于处理非活跃连接的主要采用的设计思想,具体设计细节,见源码。

三、目前只能传送文字文本,但是浏览器上的图片信息不能显示。

2018.5.22 23:00

解决办法:

查阅浏览器对html解析过程可以知道,对于图片的信息,背后其实是一个地址请求,浏览器解析到图片时,则会再次向服务器的请求得到图片资源,因此在网页传输过程中,要解决图片显示不出来,我在里的原因是没有建立对应的文件保存对应图片,因此只需要将对于图片放置在对应的文件夹,则可以正确传输。 四、建立服务器日志系统

2018.5.25

服务器日志系统加入:

服务器日志主要涉及了两个模块,一个是日志模块,一个是日志条队列模块;其中加入日志条队列模块主要是解决异步写入日志做准备,如果不用异步日志模块,则可以不用。加入异步写日志模块的主要是解决,当当单条日志比较大的时候,同步模式会阻塞整个处理流程,那么应用能处理的并发能力将有所下降,尤其是在峰值的时候,写日志可能成为系统的瓶颈,异步的好处就是在峰值的时候能够平滑过渡,降低写日志的压力。缺点就是在服务崩溃或者重启服务的时候可能会造成部分日志丢失,同步写的话就不会造成,因此可以视应用场景来选择采用写日志的主要模式。

日志模块主要采用多线程下安全的单例模式设计,可以支持自动按天分文件,按日志行数自动分文件。详细设计见源码,简单介绍下使用方法:

1.首先在主文件中初始化日志函数,可选择的参数有日志文件、日志缓冲区大小、最大行数以及最长日志条队列(如果为0采用同步写,大于0则采用异步写,其中由于STL序列中的队列函数在多线程中不安全,采用自己设计的安全队列,具体细节见源码)

2.初始化日志函数之后,便可以直接在需要输出日志的地方(如来源、错误信息等)直接使用,有两种使用方式,一是直接使用对应的宏,二是使用实例的成员函数;具体可以参考使用的源码。

五、实现一个数据库接池

2018.5.28

一个轻量级的数据库连接池:

1.设计背景

般的应用程序都会访问到数据库,在程序访问数据库的时候,每一次数据访问请求都必须经过下面几个步骤:建立数据库连接,打开数据库,对数据库中的数据进行操作,关闭数据库连接。而建立数据库连接和打开数据库是一件很消耗资源并且费时的工作,如果在系统中很频繁的发生这种数据库连接,必然会影响到系统的性能,甚至会导致系统的崩溃。

2.技术思想

在系统初始化阶段,建立一定数量的数据库连接对象(Connection),并将其存储在连接池中定义的容器中(主要利用单例设计模式,保证唯一性)。当有数据库访问请求时,就从连接池中的这个容器中拿出一个连接;当容器中的连接已经用完,就要等待其他访问请求将连接放回容器后才能使用。当使用完连接的时候,必须将连接放回容器中,这样不同的数据库访问请求就可以共享这些连接,通过重复使用这些已经建立的数据库连接,可以解决上节中说到的频繁建立连接的缺点,从而提高了系统的性能。

3.数据库连接池的主要操作

1)首先建立一个数据库连接池对象;
2)初始化数据库连接,放入连接池对象的容器中;
3)当有数据库访问请求时,直接从连接池对象的容器中得到一个连接;
4)当数据库访问完成后,应该将连接放回连接池的容器中;
5)当服务停止时,需要先释放数据库连接池中的所有数据库连接,然后再释放其对象。

六、一个简单的服务器压力测试

压力测试有很多,比较常用的是Webbench网站压测工具,可以参考我的webbench源 码包,可以直接使用。在这里,因为单纯的I/O复用方式施压程度是最高的,因为线程 和进程调度本身也要占用一定的CPU时间。因此,设计一个epoll实现的通用服务器压力 测试程序。

主要思想是先创建指定的连接数(可自己定义,程序限定的为10000),然后加入EPOLL中监听事件的可发生性,于服务器进行数据的不断交换,如果服务器足够稳定,则会一直进行下去。

通过上述两种方式测试,该服务器都能实现上千的并发连接,具体设计细节见源码

七、服务器CGI以及处理PSOT

这里利用c++基于数据库模块设计了一个简单的服务器CGI处理浏览器一些针对数据库的请求(我这里主要是验证用户名以及用户密码),然后将返回结果重新发送的服务器,服务器根据处理结果返回对应的结果给浏览器。

需要注意的是要将可执行的的CGI程序(c++)放到对应的目录处,这里是htdocs,这样才能正确执行。当然读者可以根据自己的情况设计合适的CGI程序。

About

Create a light weight web server in Linux using c/c++(利用c/c++在linux下建立一个轻量级的web服务器)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published