精华内容
下载资源
问答
  • C++web服务器开发

    千次阅读 2019-08-14 08:55:56
    C++web服务器开发 1. 首先介绍一下这个项目;这个项目是通过C++11编写的web服务器,模型为Reactor+非阻塞I/O(epoll作为I/O多路复用实现方式)+线程池,支持get、head请求,支持HTTP长连接,并实现了优雅关闭连接。 ...

    C++web服务器开发

    1. 首先介绍一下这个项目;这个项目是通过C++11编写的web服务器,模型为Reactor+非阻塞I/O(epoll作为I/O多路复用实现方式)+线程池,支持get、head请求,支持HTTP长连接,并实现了优雅关闭连接。

    I/O多路复用是什么技术呢? I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。(包括select、poll、epoll)

    文件描述符 文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
    文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

    select、poll和epoll的区别 select函数只能轮询linux操作系统提供的fd_set这个类型的变量,poll函数轮询的是pollfd这个类型的数组,这个数组可以自己定义长度,而不是上面select函数只能轮询定长为FD_SETSIZE的fd_set变量。epoll不再是轮询,而是给每个文件描述符上发生的io事件设置一个回调函数。

    2.定时器是怎么实现的?还有什么实现方式?
    定时器用来处理超时的请求和长时间不活跃的连接,使用stl里的priority_queue,基于小根堆的定时器关闭超时请求,并采用惰性删除的方式,时间的到来不会唤醒线程,而是每次循环的最后进行检查,如果超时了再删,因为这里对超时的要求并不会很高,如果此时线程忙,那么检查时间队列的间隔也会短,如果不忙,也给了超时请求更长的等待时间。还可以使用STL中的set。

    3.实现一个无锁队列? 用原子操作

    4.eventfd是什么?有什么好处?
    eventfd是系统提供的一个轻量级的进程间通信的系统调用,eventfd实现了线程的异步唤醒(eventfd()会返回一个文件描述符,如果该进程被fork的时候,这个文件描述符也会复制过去,这时候就会有多个的文件描述符指向同一个eventfd对象)

    5.双缓冲区异步日志是什么?为什么要这样做?对这个日志系统有没有进行压力测试?
    基本思路是准备两块buffer:A和B, 前端负责往buffer A填数据(日志消息), 后端负责将buffer B的数据写入文件;当buffer A写满之后, 交换A和B, 让后端将buffer A的数据写入文件, 而前端则往buffer B填入新的日志消息, 如此往复。
    好处:前端不是将一条条日志消息分别送给后端,而是将多条日志消息拼接成一个大的buffer传送给后端,相当于批处理,减少了线程唤醒的开销。

    6.什么是优雅关闭连接?
    就是read()到0,要透明的传递这个行为而不是直接暴力close()

    7.epoll的边沿触发(et)和水平触发(lt)有什么区别? lt模式下,默认不可读,只有epoll通知你可读才是可读,否则不可读。
    et模式下,默认可读。你可以随便读,直到发生EAGAIN。可读时读和不读,怎么读都由你自己决定,中间epoll不管。
    EAGAIN后不可读了,等到再次可读,epoll会再通知一次。
    ET模式要比LE复杂许多,它对用户提出了更高的要求,即每次读,必须读到不能再读(出现EAGAIN),每次写,写到不能再写(出现EAGAIN)。而LT则简单的多,可以选择也这样做,也可以为编程方便,比如每次只read一次(muduo就是这样做的,这样可以减少系统调用次数)。

    8.epoll为什么高效,相比select和poll

    1. 减少了用户态和内核态之间的文件描述符拷贝
    2. 减少了对就绪文件描述符的遍历

    9.假如服务器要升级,又不想让用户感觉到服务器升级了,该怎么做?
    不间断的提供服务,参考nginx的平滑升级

    10.Reactor模式是什么?
    反应器设计模式(Reactor pattern)是一种为处理并发服务请求,并将请求提交到一个或
    者多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有的请求,然后派发这些请求至相关的工作线程进行处理。

    展开全文
  • Linux下Web服务器开发

    千次阅读 2016-07-10 13:53:14
    web服务器开发,练习指南。

    代码下载地址:http://download.csdn.net/detail/u010959074/9572149

    以下是项目介绍。

    学习提示:

    1. 桌面环境中动手练习,若环境不流畅可选择WebIDE或字符界面。

    2. 在教程下方课程问答中提出问题,或共享桌面寻求远程帮助。

    3. 在教程下方实验报告中完成作业,记录心得。公开报告可以获得大家点评。

    4. 我的代码库中用GIT提交你的实验代码。

    Web服务器

    The way to learn a programming language is to write programs.

    这门项目训练营最大的价值是实验楼和教师共同提供的教学服务,目的只有一个:让你把项目做出来并完全理解。所以请对自己负责,提出你遇到的任何问题,完成项目,你所花费的时间才有价值!

    一、实验说明

    欢迎参加C++语言经典项目实战训练营,在四周的时间里我们将一起完成四个小型及中型C++语言项目,涉及到C++语言技术的多方面。为了你能够有所收获,学习过程中务必注意:

    进度很重要:必须跟上每周的进度,项目,问答。我们会认真对待每一位参与训练营的同学,请你不要因为困难半途而废。

    问答很重要:遇到知识难点请多多提问,这是你的权利更是对自己负责的义务。

    实践很重要:完成每周项目,解决项目中出现的一切问题。项目都提供完整的代码,但仅供学习中参考,应按照实验文档的思路自己实现,请勿直接拷贝代码。

    实验报告很重要:详细记录你完成项目任务和解决问题的思路。

    反馈很重要:请务必将你对课程的建议告诉我们,不同于视频,我们的文档可以很快更新升级以满足你的学习需求。

    代码练习说明

    实验楼环境使用GCC/G++ 4.8.4 编译环境。C++代码采用C++11标准。编写可以使用vim或gedit,并使用Git进行代码管理,实验报告需要以Markdown格式编写并公开。

    这些技术都是很多程序员工作中天天用到的,如果你对其中任何知识不熟悉,可以先学习课程:

    Linux基础入门

    Vim编辑器

    Git与实验楼代码库

    Markdown与实验报告

    如果需要保存代码到我的代码库,则需要用到git,目前实验楼的代码库可以在实验环境或者你自己的电脑上提交,请务必注意git push时输入的用户名是你登陆实验楼的邮箱(不是你代码库用户名),如果你是第三方登陆的,则需要先更新邮箱及登录密码后才可以在实验楼外部使用。

    程序编写与编译最基本的方法(#标志后为注释):

    # 打开桌面上的Xfce终端并执行后续命令# 进入到/home/shiyanlou目录cd /home/shiyanlou# 创建并打开源文件hello.cpp

    gedit hello.cpp# 在gedit(类似windows上的记事本)中输入代码# 保存退出# 编译hello.cpp

    g++ -std=c++11 hello.cpp -o hello# 执行生成的二进制文件

    ./hello

    二、项目简介

    1. 介绍

    本实验使用 C++ 实现一个Web服务器。

    这个项目会学习C++网络开发,kqueue IO复用机制,熟悉Linux下的C++程序编译方法,Makefile编写。

    本节实验项目比较复杂,提供的示例代码接近3000行,但代码逻辑简单,难点在kqueue IO复用机制的理解,尽量独立实现,遇到难点可以参考示例代码或到实验楼问答中提问。

    2. 项目需求

    编写一个Web服务器,程序具备的基本功能:

    支持多个客户端连接

    支持HTTP协议中的GETHEADOPTION三个方法

    支持通过浏览器访问多种类型的文件资源

    支持多虚拟主机,每个虚拟主机可以配置独立的资源池

    3. 知识点

    本实验实践的知识点包括:

    C++ 面向对象程序设计

    基本的Makefile

    C++ 网络编程

    kqueue IO复用机制

    HTTP 协议基础

    4. 项目效果图

    程序运行的截图如下所示:

    5. 项目完整代码

    项目完整代码可以通过wget下载获得:

    # 下载程序代码wget http://labfile.oss.aliyuncs.com/courses/454/webserver.zip

    # 解压代码

    unzip webserver.zip

    # 进入代码文件夹查看

    cd webserver

    三、程序设计与实现

    3.1 需求分析

    大家平时上网时,浏览器中显示的HTML网页都是来源自Web服务器。本项目通过一个实例介绍如何实现一个简单的Web服务器。

    本项目中实现的Web服务器需要支持下面几个功能:

    支持多个客户端连接

    支持HTTP协议中的GETHEADOPTION三个方法

    支持通过浏览器访问多种类型的文件资源

    支持多虚拟主机,每个虚拟主机可以配置独立的资源池

    实现具备这些基本功能的Web服务器,我们首先需要了解HTTP协议以及IO复用机制,在上一节的项目中我们已经学习了一种IO复用方法epoll,本项目采用kqueue机制,与epoll非常类似。

    3.2 HTTP协议

    HTTP协议介绍的好文章非常多,这里只列出项目中需要的知识点,推荐大家系统学习HTTP协议,请阅读:

    HTTP协议详解

    HTTP协议的一个主要特点就是客户端服务器模式,在上一个项目中我们有过介绍,基于socket的连接过程,如果大家没有印象可以去复习下。过程如下:

    模型如下:

    解释如下:

    服务器端:

    socket()创建监听Socket

    bind()绑定服务器端口

    listen()监听客户端连接

    accept()接受连接

    recv/send接收及发送数据

    close()关闭socket

    客户端:

    socket()创建监听Socket

    connect()连接服务器

    recv/send接收及发送数据

    close()关闭socket

    其实实现的Web服务器是一个支持HTTP协议的TCP服务器。所以服务器启动过程仍然与上述过程相同,listen后等待客户端的连接。连接后开始进行HTTP协议的通信,通过请求与响应来建立Web访问。

    HTTP的请求

    HTTP请求由客户端发给服务器端,分三部分组成,分别是:请求行、消息报头、请求正文。

    请求方法(所有方法全为大写)有多种,各个方法的解释如下:

    GET 请求获取Request-URI所标识的资源

    POST Request-URI所标识的资源后附加新的数据

    HEAD 请求获取由Request-URI所标识的资源的响应消息报头

    PUT 请求服务器存储一个资源,并用Request-URI作为其标识

    DELETE 请求服务器删除Request-URI所标识的资源

    TRACE 请求服务器回送收到的请求信息,主要用于测试或诊断

    CONNECT 保留将来使用

    OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项和需求

    项目里暂时实现了GET/HEAD/OPTION的处理,有兴趣的可以根据这三个处理函数的示例实现其他的。

    HTTP的响应

    在接收和解释请求消息后,服务器返回一个HTTP响应消息。HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文。

    状态行格式:HTTP-Version Status-Code Reason-Phrase CRLF,其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。

    状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

    1xx:指示信息--表示请求已接收,继续处理

    2xx:成功--表示请求已被成功接收、理解、接受

    3xx:重定向--要完成请求必须进行更进一步的操作

    4xx:客户端错误--请求有语法错误或请求无法实现

    5xx:服务器端错误--服务器未能实现合法的请求

    当客户端与Web服务器建立连接后就会通过发送请求接收服务器响应来进行通信。而服务器端如果要处理多个客户端的请求,可以通过epollkqueueIO机制来获得读写的消息通知,本项目采用kqueue实现。

    3.4 kqueue 机制

    kqueueepoll非常相似,最初是2000Jonathan LemonFreeBSD系统上开发的一个高性能的事件通知接口。注册一批socket描述符到kqueue 以后,当其中的描述符状态发生变化时,kqueue将一次性通知应用程序哪些描述符可读、可写或出错了。

    如果你已经理解了聊天室项目中介绍的epoll,那kqueue就很好理解,kqueue提供 kqueue()kevent()两个系统调用和 struct kevent结构。

    kqueue机制详细介绍,推荐大家阅读下面的理论文章,虽然讲的是FreeBSD上的开发但在Linux上完全适用:

    使用kqueue FreeBSD 上开发高性能应用服务器

    kqueue的接口包括 kqueue()kevent()两个系统调用和 struct kevent结构:

    kqueue() 生成一个内核事件队列,返回该队列的文件描述索。其它 API通过该描述符操作这个 kqueue

    kevent() 提供向内核注册 /反注册事件和返回就绪事件或错误事件。

    struct kevent 就是kevent()操作的最基本的事件结构。

     struct kevent {

         uintptr_t ident;       /* 事件 ID */ 

         short     filter;       /* 事件过滤器 */ 

         u_short   flags;        /* 行为标识 */ 

         u_int     fflags;       /* 过滤器标识值 */ 

         intptr_t  data;         /* 过滤器数据 */ 

         void      *udata;       /* 应用透传数据 */ 

     };

    项目中我们会用到的内容包括事件过滤器,可以将 kqueue filter 看作事件。内核检测 ident上注册的 filter的状态,状态发生了变化,就通知应用程序。kqueue定义了较多的 filter,本文只介绍Socket 读写相关的filter

    EVFILT_READTCP监听 socket,如果在完成的连接队列( 已收三次握手最后一个ACK) 中有数据,此事件将被通知。收到该通知的应用一般调用accept(),且可通过data 获得完成队列的节点个数。 流或数据报socket,当协议栈的socket 层接收缓冲区有数据时,该事件会被通知,并且data 被设置成可读数据的字节数。

    EVFILT_WRIT:当 socket层的写入缓冲区可写入时,该事件将被通知;data指示目前缓冲区有多少字节空闲空间。

    行为标志flags

    EV_ADD:指示加入事件到 kqueue

    EV_DELETE:指示将传入的事件从 kqueue中移除

    过滤器标识值:

    EV_ENABLE:过滤器事件可用,注册一个事件时,默认是可用的。

    EV_DISABLE:过滤器事件不可用,当内部描述可读或可写时,将不通知应用程序。

    这些参数都会在Web服务器实现中使用。当socket有连接或读写数据事件时,服务器读取客户端请求并将响应写回到客户端。kqueue充当的是事件触发的中间层。

    为了我们可以使用kqueue,首先要在系统中安装libkqueue

    sudo apt-get update

    sudo apt-get install libkqueue-dev

    编译链接时也要在g++后添加-lkqueue才可以正常链接。

    3.5 程序设计

    根据上面的需求分析,设计所需的类。

    首先需要一个HTTPServer类,用来提供服务器的启动关闭主循环等支持。HTTPServer对象在程序中唯一。

    其次需要Client客户端类,这个类用来存储客户端的必要信息。HTTPServer对象中需要包含多个当前连接的Client对象。

    再次,对于ClientHTTPServer之间的通信内容需要设置HTTPRequestHTTPResponse类,而这两个类具备很多通用的信息,设计二者的基类为HTTPMessage类。

    最后HTTPServer需要能够返回服务器资源给客户端,因此需要资源类Resource(文件,图片等文件)以及包含资源的资源池ResourceHost。这里可以为不同的虚拟主机设置不同的ResourceHost

    上面的几个类是Web服务器构成的主体,其中HTTPRequestHTTPResponseHTTPMessage三个类都是用来存储消息, 而客户端与服务器之间的消息都是以队列的方式进行读取和写入的,因此在HTTPMessage的上一层,我们添加一个新的基类ByteBuffer来实现 基础字节流的存储和读写处理。

    综上项目中实现下面的类:

    ByteBuffer:定义一个缓存队列用来存储数据

    HTTPMessage:继承ByteBuffer,增加HTTP协议特有的信息。

    HTTPRequestHTTP请求类,继承HTTPMessage

    HTTPResponseHTTP响应类,继承HTTPMessage

    Resource:资源类,HTTP响应消息中的文件资源数据

    ResourceHost:资源池,用来将服务器上的文件加载到Resource对象

    Client:客户端类,用来存储客户端的必要信息

    HTTPServer:服务器类,提供服务器的启动关闭主循环等支持

    3.6 代码结构

    根据上述细化需求,我们先创建必要的程序文件。

    首先为要实现的程序命名为webserver:

    # 进入到代码库自动生成的目录# 为了方便git提交代码请先开通代码库

    cd /home/shiyanlou/Code/shiyanlou_cs454

    # 创建代码目录mkdir webserver

    cd webserver

    # 为每个需要实现的类创建一个.h和一个.cpp文件# 并将源文件都放在src/目录下mkdir src# touch src/XXX.h src/XXX.cpp# 创建Makefile

    touch Makefile

    # 创建示例目录,该目录可以作为Web服务器的资源池mkdir bin/mkdir bin/htdoc

    touch bin/htdoc/test.html

    本项目参考代码基于https://github.com/RamseyK/httpserver进行实现。每个头文件中的函数都进行了标注,核心模块HTTPServer.cpp也会进行详细介绍。其他cpp文件由于内容相对简单,直接使用原作者的英文注释,如果有任何不清楚的问题可以随时在实验楼问答提问。

    下面我们将开始实现需要的类,建议大家按照以下步骤先自己实现,最后再对项目参考代码进行对照学习。

    3.7 ByteBuffer

    ByteBuffer 定义一个缓存队列用来存储HTTP访问中的请求及返回数据,这个缓存队列的作用相当于CPU和硬盘之间的内存,用来存放Web服务器和客户端之间通信的数据,需要具备下面的功能:

    从队列头部读取数据

    向队列尾部写入新的数据

    当队列存储区域满的时候可以自动扩充

    支持随机读取和写入多种数据类型到指定位置(使用模板实现)

    支持清空,克隆,对比,扩容

    支持遍历队列查找指定的数据

    ByteBuffer的核心接口如下:

    class ByteBuffer {

        // 读写位置索引

        unsigned int rpos, wpos;

     

        // 缓存内容使用容器存储

        std::vector<byte> buf;

     

        // 队列中剩余的元素数量

        unsigned int bytesRemaining();

     

        // 清空队列并重置读写索引

        void clear();

     

        // 拷贝当前ByteBuffer对象

        ByteBuffer* clone();

     

        // 对比两个ByteBuffer对象

        bool equals(ByteBuffer* other);

     

        // 设置存储空间大小

        void resize(unsigned int newSize);

     

        // 返回存储空间大小

        unsigned int size();

     

        // 读取队列头部的数据但不移动rpos

        byte peek();

        // 读取队列头部的数据同时移动rpos

        byte get();

        // 读取指定位置的数据

        byte get(unsigned int index);

        // 读取队列头部指定长度的数据到buf中

        void getBytes(byte* buf, unsigned int len);

        // 读取指定数据类型的元素:char,double,float,int,long,short

        char getChar();

        char getChar(unsigned int index);

     

        // 将src写入队列

        void put(ByteBuffer* src);

        // 将b写入队列

        void put(byte b);

        // 将b写入队列指定位置

        void put(byte b, unsigned int index);

        // 将长度为len的缓存b写入队列头部

        void putBytes(byte* b, unsigned int len);

        // 将长度为len的缓存b写入队列指定位置

        void putBytes(byte* b, unsigned int len, unsigned int index);

        // 写入指定数据类型的元素:char,double,float,int,long,short

        void putChar(char value);

        void putChar(char value, unsigned int index);     

     

    }

    看上去非常繁琐,但可以使用模板实现基础函数,大部分函数都能够通过调用函数模板的方式简化实现内容。

    3.8 HTTPMessage

    继承ByteBuffer实现HTTPMessage类。这个类需要包含:

    HTTP头部

    HTTP版本号

    消息数据:包括ByteBuffer中的数据及额外的数据(响应消息中返回的资源及请求消息中额外的参数)

    HTTP消息解析函数,根据HTTP协议的规范对消息头部和内容进行解析

    HTTP消息创建函数,创建符合HTTP协议要求的HTTP消息

    HTTP头部管理函数,添加HTTP头部信息

    这个类比较简单,只需要把上述几个函数依次实现,核心函数包括:

    class HTTPMessage : public ByteBuffer {private:

     

        // 消息头部信息

        std::map<std::string, std::string> *headers;

    protected:

        // HTTP版本号

        std::string version;

     

        // 消息数据

        // 响应消息中表示资源,请求消息中表示额外的参数

        byte* data;

    public:

     

        // 创建消息

        virtual byte* create() = 0;

     

        // 解析消息

        virtual bool parse() = 0;

     

    };

    3.9 HTTPRequest

    HTTP请求类继承HTTPMessage,需要实现以下数据项及函数功能:

    请求的方法:GET/HEAD/OPTION

    请求的URI

    实现HTTPMessage的创建和解析虚函数

    核心成员声明:

    // HTTPRequest:HTTP请求消息,从客户端发给服务器的消息class HTTPRequest : public HTTPMessage {private:

     

        // 请求的方法类型

        int method;

     

        // 请求的URL

        std::string requestUri;

    protected:

     

        // 初始化消息

        virtual void init();

    public:

        virtual byte *create();

        virtual bool parse();

     

        // 辅助函数

        // 方法字符串与数字相互转换

        int methodStrToInt(std::string name);

        std::string methodIntToStr(unsigned int mid);

     

    };

    3.10 HTTPResponse

    HTTP响应类继承HTTPMessage,需要实现以下数据项及函数功能:

    响应状态码和信息

    实现HTTPMessage的创建和解析虚函数

    核心成员声明:

    // HTTPResponse:HTTP响应消息,从服务器发给客户端的消息class HTTPResponse : public HTTPMessage {private:    

        // 响应状态码和信息

        int status;

        std::string reason;

     

    protected:

        virtual void init();

    public:

        virtual byte* create();

        virtual bool parse();

     

    };

    3.11 Resource

    HTTP响应消息中的Resource除了包含文件数据以外,最重要的特点是具备mimeType标识,MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。

    Resource类中需要的成员:

    资源数据

    数据大小

    数据MIME类型

    资源在服务器上存储的路径

    核心成员声明如下:

    // Resource:HTTP响应消息中的文件资源数据class Resource {

    private:

     

        // 文件资源

        byte* data;

        // 资源大小

        unsigned int size;

        // 资源类型

        std::string mimeType;

        // 资源在服务器上存储的路径

        std::string location;

        // 是否是目录类型

        bool directory;

     

    };

    3.11 ResourceHost

    ResourceHost是一个Resource的管理组件,可以通过一个目录来构建。需要支持的成员包括:

    基础目录,其他的访问路径都是该目录下的相对路径

    根据访问地址URI获取Resource资源对象

    将资源写入到文件系统,用来支持上传操作

    Resource中的MimeType的获取可以通过判断扩展名来实现,需要构建扩展名和MimeType的映射表。

    我们需要定义一个数据结构包含所有的MIME信息,示例代码中采用宏的方式从文件MimeTypes.inc中读取:

    // 构建扩展名与MimeType对应的字典,从文件MimeTypes.inc中获取

    std::unordered_map<std::string, std::string> mimeMap = {

        #define STR_PAIR(K,V) std::pair<std::string, std::string>(K,V)

        #include "MimeTypes.inc"

    };

    MimeTypes.inc中的内容是若干行:

    STR_PAIR("bmp", "image/bmp"),

    STR_PAIR("book", "application/book"),

    ...

    核心成员声明如下:

    // ResourceHost:资源池,用来将服务器上的文件加载到Resource对象class ResourceHost {private:

        // 基础目录,其他的访问路径都是该目录下的相对路径

        std::string baseDiskPath;

     

        // 构建扩展名与MimeType对应的字典,从文件MimeTypes.inc中获取

        std::unordered_map<std::string, std::string> mimeMap = {

            #define STR_PAIR(K,V) std::pair<std::string, std::string>(K,V)

            #include "MimeTypes.inc"

        };

    public:

        // 将资源写入到文件系统

        void putResource(Resource* res, bool writeToDisk);

     

        // 根据URI获取Resource资源对象

        Resource* getResource(std::string uri);

    };

    3.12 Client

    客户端的数据会存放在HTTPServer中的一个映射表中,用客户端连接的socketkey。每个客户端对象都应该包含客户端的socketsockaddr_in地址信息,数据发送队列。

    客户端的操作需要支持添加新的数据到发送队列,对发送队列进行出队,清空等操作。

    发送队列使用std::queue,每个存储单元需要单独设计要包含发送的数据及数据偏移,并要增加是否需要在发送完成后断开连接的标志。发送存储单元命名为SendQueueItem类,在Client类声明中需要使用。

    核心成员声明如下:

    // HTTP客户端// class Client {

     

        // 连接的socket

        int socketDesc;

     

        // 地址信息

        sockaddr_in clientAddr;

     

        // 数据发送队列

        std::queue<SendQueueItem*> sendQueue;

    public:

     

        // 发送队列操作

        // 添加新的数据到发送队列

        void addToSendQueue(SendQueueItem* item);

     

        // 发送队列长度

        unsigned int sendQueueSize();

     

        // 获取发送队列中第一个元素

        SendQueueItem* nextInSendQueue();

     

        // 出队操作,删除第一个元素

        void dequeueFromSendQueue();

     

        // 清空发送队列

        void clearSendQueue();

    };

    3.13 HTTPServer

    这是整个项目的核心,具有最复杂的类和实现过程。

    首先分析HTTPServer需要具备的数据成员,刚才已经提到Client客户端列表是必须的,此外还需要包含:

    监听的端口号

    监听的socket

    服务器地址信息

    kqueue 描述符

    kevent 事件队列

    客户端字典,映射客户端的socket和客户端对象

    资源主机及文件系统列表

    虚拟主机,映射请求的地址到不同的ResourceHost

    根据需求中HTTPServer需要支持下面几大类功能:

    处理客户端连接

    处理客户端请求

    发送响应消息给客户端

    服务器管理

    每个功能集合中又可以细分出不同的功能。

    处理客户端连接

    接受连接:accept接受连接并将新的socket添加到kqueue中,创建客户端对象添加到客户端列表中

    获取客户端对象:根据socket查找客户端列表返回客户端对象

    断开连接:清理kqueue中的socket,关闭socket,清理客户端列表中的对象

    处理客户端读取事件:recv()接收客户端数据并构造HTTPRequest对象,交给handleRequest()函数处理。

    处理客户端写入事件:获取发送队列中的SendQueueItemsend()发送给客户端

    处理客户端请求

    处理请求:对请求方法进行分类分别交给不同的处理函数

    处理GET/HEAD/OPTIONS请求:构造HTTPResponse,调用sendResponse发送给客户端

    发送响应消息给客户端

    发送响应状态码:构造HTTPResponse,调用sendResponse发送给客户端

    发送响应消息:将HTTPResponse封装后添加到客户端的发送队列

    服务器管理

    启动:包含各种数据和资源的初始化,监听socket的建立,绑定,kqueue的创建和初始化。

    停止:释放资源,清理客户端列表,清理kqueue,清理socket

    主循环:进入事件循环,等待kqueue事件触发,有事件触发后需要先判断是新的客户端连接还是客户端读写事件

    核心成员声明如下:

    // HTTPServer:Web服务器类,提供服务器的创建,启动,停止等管理操作class HTTPServer {    

        // 监听的端口号

        int listenPort;

     

        // 监听的socket

        int listenSocket;

     

        // 服务器地址信息

        struct sockaddr_in serverAddr;

     

        // kqueue 描述符

        int kqfd;

     

        // kevent队列

        struct kevent evList[QUEUE_SIZE];

     

        // 客户端字典,映射客户端的socket和客户端对象

        std::unordered_map<int, Client*> clientMap;

     

        // 资源主机及文件系统列表

        std::vector<ResourceHost*> hostList;

     

        // 虚拟主机,映射请求的地址到不同的ResourceHost

        std::unordered_map<std::string, ResourceHost*> vhosts;

     

        // 处理客户端连接

        void acceptConnection();

        void disconnectClient(Client* cl, bool mapErase = true);

        void readClient(Client* cl, int data_len);

        bool writeClient(Client* cl, int avail_bytes);

     

        // 处理客户端请求

        void handleRequest(Client* cl, HTTPRequest* req);

        void handleGet(Client* cl, HTTPRequest* req, ResourceHost* resHost);

        void handleOptions(Client* cl, HTTPRequest* req);

     

        // 发送响应消息给客户端

        void sendStatusResponse(Client* cl, int status, std::string msg = "");

        void sendResponse(Client* cl, HTTPResponse* resp, bool disconnect);

     

        // 启动及停止Web服务器

        bool start(int port);

        void stop();

     

        // Web服务器主循环

        void process();

    };

    HTTPServer启动时我们默认设置当前路径下的htdoc文件夹为ResourceHost,并添加到HTTPServer对象中。而htdoc下我们添加一个test.html文件,作为可以访问的Resourcetest.html中的内容可以很简单,例如:

    <html><head><title>shiyanlou demo site</title></head>

    <body>

    Hello Shiyanlou!<br /></body></html>

    这个示例页面可以在后面的测试中通过访问localhost:8080/test.html访问到。

    3.14 主函数

    主函数中实现包括两部分内容:

    注册各种信号处理函数,能够让Web服务中止时可正常释放资源

    创建HTTPServer对象,依次启动,进入主循环

    需要处理的包括中断信号SIGABRTSIGINTSIGTERM,这些信号发生时要将HTTPServer的停止标志置为True,下次循环时就可以退出。SIGPIPE信号需要被忽略,避免"Broken pipe"出现。

    示例代码片段:

    // 忽视 SIGPIPE "Broken pipe" 信号

    signal(SIGPIPE, handleSigPipe);

    // 注册中断处理

    signal(SIGABRT, &handleTermSig);

    signal(SIGINT, &handleTermSig);

    signal(SIGTERM, &handleTermSig);

    // 创建并启动HTTPServer实例

    svr = new HTTPServer();

    svr->start(8080);

    // 进入主循环

    svr->process();

    // 停止服务器

    svr->stop();

    delete svr;

    3. 编译及运行

    将你完成的文件保存为/home/shiyanlou/Code/shiyanlou_cs454/webserver,在同样的目录下我们编辑Makefile文件:

    cd /home/shiyanlou/Code/shiyanlou_cs454/webserver

    vim Makefile

    Makefile文件里的内容:

    CC := g++SRCDIR := srcBINDIR := binBUILDDIR := buildTARGET := httpserverUNAME := $(shell uname)

    # Debug FlagsDEBUGFLAGS := -g3 -O0 -WallRTCHECKS := -fmudflap -fstack-check -gnato

    # Production FlagsPRODFLAGS := -Wall -O2

    CFLAGS := -std=c++11 -Iinclude/ $(DEBUGFLAGS)LINK := -lpthread -lkqueue $(DEBUGFLAGS)

    SRCEXT := cppSOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.o))

    $(TARGET): $(OBJECTS)

        @echo " Linking..."$(LINK); $(CC) $^ -o $(BINDIR)/$(TARGET) $(LINK)

    $(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT)

        @mkdir -p $(BUILDDIR)

        @echo " CC $<"; $(CC) $(CFLAGS) -c -o $@ $<

    clean:

        @echo " Cleaning..."; rm -r $(BUILDDIR) $(BINDIR)/$(TARGET)*

     

    .PHONY: clean

    Makefile内容很多,大部分都是变量定义,核心内容只有下面两行:

    @echo " CC $<"; $(CC) $(CFLAGS) -c -o $@ $< 编译每个CPP文件,生成.o目标文件

    $(CC) $^ -o $(BINDIR)/$(TARGET) $(LINK) 链接上一步骤生成的所有目标文件,得到可执行的httpserver文件

    保存Makefile后,我们只需要在目录下执行make就可以生成可执行文件httpserver。

    编译过程截图:

    Makefile会把可执行文件放到了bin/目录下,因为bin/目录下的htdoc/已经写在代码中作为默认ResourceHost了,所以测试启动后的Web服务器可以访问htdoc下的文件。

    现在进入运行测试阶段,首先启动服务端:

    cd bin/

    ./httpserver

    运行截图如下:

    如果中间有任何问题,需要根据输出的错误信息查验下代码是否有BUG。欢迎随时到实验楼问答提问。

    3.8 完整代码参考

    我们提供本项目完整的代码及详细注释供参考,由于代码比较多,文档中不再粘贴,建议大家下载查看。

    # 下载程序代码wget http://labfile.oss.aliyuncs.com/courses/454/webserver.zip

    # 解压代码

    unzip webserver.zip

    # 进入代码文件夹查看

    cd webserver

    本项目参考代码基于https://github.com/RamseyK/httpserver修改。代码License仍然遵循Apache License, Version 2.0

    四、项目扩展

    本实验实现了一个Web服务器程序。基于本课节学习,大家可以在此代码基础上实现扩展:

    支持更多的HTTP请求方法,例如POST

    支持配置文件,比如配置多个虚拟主机,资源池,端口号等

    支持PHP页面解析,可以加入单独的模块

    五、小结

    通过本节实验的学习,我们开发了Web服务器程序,学习了C++语言的基本语法及面向对象开发,网络程序开发,HTTP协议及kqueue IO复用机制。

    完成项目后可以公开你的实验报告,并点击实验报告下方的分享按钮分享到微博,将会获得教师点评,同时优秀的实验报告官微将转发推荐!

    再次提醒,任何问题欢迎到实验楼问答中提问,老师会及时回答你的困惑。

    特别说明:

    实验作业与学习心得请写在下方实验报告(支持markdown格式)里并公开,每周选取优秀报告奖励IT书籍!

    您已经完成本课程所有实验

     

    展开全文
  • 本章主要讲解如何购买和配置阿里云ECS服务器以及搭建网络站点。
    本章主要讲解如何购买和配置阿里云ECS服务器以及搭建网络站点。
    

    一 阿里云ECS服务器

    (一)购买阿里云服务器(只要实名认证24岁下,可以直接购买学生机)

    1 注册阿里云,这步就不细说了。
    2 配置ECS服务器
    (1):在这里插入图片描述
    (2):
    在这里插入图片描述
    (3)选择收费方式,配置CPU
    在这里插入图片描述
    (4)选择开发系统以及存储盘
    在这里插入图片描述
    (5) 配置网络带宽,安全组端口,然后下一步
    在这里插入图片描述
    (6) 为系统用户配置密码,记录下用户名和密码,然后下一步
    在这里插入图片描述
    (7) 下一步下单,然后购买。购买后
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    (8)记录下ip地址,到这里服务器购买结束。

    (二)连接阿里云服务器

    1 win+r,打开运行,出入mstsc
    在这里插入图片描述
    2 输入刚才记录下的公网ip地址
    在这里插入图片描述
    3 实现本地资源与服务器的共享
    在这里插入图片描述
    4 连接
    在这里插入图片描述
    5 输入记录下来的用户密码
    在这里插入图片描述
    6 连接成功在这里插入图片描述
    在这里插入图片描述

    (三)阿里云服务器配置http服务器

    1.安装WEB服务器
    (1)打开服务器管理器,选择添加角色和功能在这里插入图片描述
    (2) 一直下一步直到选择web服务器,其他默认,应用程序开发全选,以防开发以后需要。
    在这里插入图片描述
    (3)添加功能
    在这里插入图片描述
    (4)安装,等待一会。
    2.配置IIS
    (1)打开IIS
    在这里插入图片描述
    (2)可以看到已经有默认的站点
    在这里插入图片描述
    (3)新建文件夹,新建htm文件
    在这里插入图片描述
    (4)右键网站,添加网站,名称随意,应用池选择默认,路径选择刚才新建的文件夹,ip地址这里输入记录下的私网ip地址,端口写8080
    在这里插入图片描述
    这时候虽然建好了站,但是由于阿里云服务器的安全组没有开放8080端口,随意还无法访问。

    (四)阿里云服务器配置安全组规则

    1.在这里插入图片描述
    2.添加安全组规则
    在这里插入图片描述
    好了,现在可以在本地计算机访问刚刚建立的网站,注意这里使用公网ip地址。

    在这里插入图片描述
    下章将介绍如何配置安全域名,备案域名时间有些长。

    展开全文
  • 下载花生壳在花生壳官网下载页面,选择“树莓派”,点击立即下载。 图1 下载链接:... 1.安装dpkg。...在上文中将下载后包复制到树莓派上,通过cd命令进入对应下载目录,输入下面的命令

    下载花生壳

    在花生壳官网下载页面,选择“树莓派”,点击立即下载。

    这里写图片描述

    图1

    下载链接:http://hsk.oray.com/download/

    安装与使用

    注意:花生壳安装步骤都需要在管理员(Root)权限下运行。
    1.安装dpkg。(非必须)
    sudo apt-get install dpkg

    2.下载安装包
    在上文中将下载后包复制到树莓派上,通过cd命令进入对应下载目录,输入下面的命令进行安装:
    sudo dpkg -i phddns_rapi_3.0.1.armhf.deb
    安装成功后,将显示此树莓派唯一的SN码、默认密码以及远程管理地址。

    这里写图片描述

    图2

    3.卸载
    如需卸载安装使用一下命令。
    sudo dpkg -r phddns

    4.其他
    输入phddns回车后,可以看到扩展的功能:
    sudo phddns start(启动)| stop(停止)| restart(重启)
    sudo phddns status(状态)| version(版本)|reset(重置)

    5.日志
    花生壳日志文件存放路况:/var/log/phddns

    配置花生壳

    1.浏览器输入远程管理地址b.oray.com进入花生壳远程管理页面
    输入安装花生壳时生成的SN码及默认密码admin进入;

    这里写图片描述

    图3

    2.首次登录,需要进行初始化:重设密码,填写手机,发送验证码;

    这里写图片描述

    图4

    3.初始化成功,赠送一个免费域名;

    这里写图片描述

    图5

    4.注意:默认内置帐号只有公网版服务,如需使用内网穿透功能,要自行开通;

    这里写图片描述

    图6

    5.点击立即开通则调整至内网穿透服务开通页面;

    这里写图片描述

    图7

    6.若之前已在官网注册了花生壳帐号,也可通过更换登录,登录你自己注册的帐号来使用。

    这里写图片描述

    图8
    展开全文
  • web开发---为什么要安装web服务器

    千次阅读 2016-10-17 16:20:53
    做Web开发为什么要安装web服务器(tomcat、weblogic等)?web资源可以是静态的也可以是动态的。 用静态的举例。 存在一个静态资源我在web文件夹中有一个index.html文件 文件里面内容如下:web resource index那么...
  • 目标用已有的丰富图片资源建一个看图网站条件开发语言:python3库:flask :一个开源的python web服务器框架 jinja2:flask默认的模板引擎编辑器:推荐pycharm一个最简单的web服务器python给我们提供了一个接口:WSGI...
  • Web开发中常用的Web应用服务器

    千次阅读 2018-09-22 23:07:32
    Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上Nginx的并发能力确实在同类型的网页服务器中表现较好,中国...
  • 什么是HTTP协议? 客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守
  • 该文章,只用于学习顺序方面的引导,有利于初学者快速找到方向,技术牛们见笑了!!!(web开发,python,django,IIS,mysql)
  • WEB开发的相关知识WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源。 Internet上供外界访问的Web资源分为: 静态web资源(如html 页面):指web页面中供人们浏览的数据始终是不变的...
  • 1、高性能Web服务器Nginx的配置与部署研究(1)Nginx简介及入门示例内容:概述Nginx的背景知识和简单的入门实例。2、高性能Web服务器Nginx的配置与部署研究(2)Nginx入门级配置与部署及“Hello World”内容:简述...
  • 无法连接到VS2010web开发服务器

    千次阅读 2014-11-15 11:12:46
    原因:VS的WEB服务器自动分配的端kkkk
  • web服务器与web框架

    千次阅读 2019-01-17 10:02:31
    Web 服务器 当我们在浏览器输入URL后,浏览器会先请求DNS服务器,获得请求站点的 IP 地址。...这个过程中,正是Web服务器在幕后默默做贡献。...简单来说,Web服务器是在运行在物理服务器上的一个程序,它永久地等待客户...
  • Go语言也被成为“自带电池”的语言,有大量的web相关工具集成在其中,构建web应用成了一件信手拈来的事情,只要调用http包的两个函数就可以了。package mainimport ( "fmt" "net/http" "log" )func responseHello...
  • 一台服务器,接待用户请求的就是 Web 服务器,是一种软件,比如 Apache,Nginx。Web 服务器除了为用户提供静态的文件,像 html,CSS,JavaScript,图像文件 等等,它还会跟脚本解释器沟通,给用户提供动态的内容。 ...
  • web服务器、Web中间件和Web容器的区别

    万次阅读 多人点赞 2018-11-25 20:36:21
    Web服务器 中间件 容器 总结 Web开发的选择 我们经常会被Web服务器、Web容器和Web中间件这三个概念搞混。因为我们常见的很多网站要么是由IIS搭建,要么是由Apache、Tomcat、Ngnix搭建。所以,我们会把他们都叫...
  • web服务器和web应用服务器的区别

    千次阅读 2018-06-10 20:22:43
    在java web开发时,最早接触的web服务器是tomcat,其实tomcat是web应用服务器,任何的 web项目资源文件如果没有部署 在tomcat应用服务器中(资源文件没有放在tomcat安装目录中),都将不能访问得到。 类似的web应.....
  • 自从Node.js出现之后,真的是繁荣了整个前端开发圈子,搭建web服务器也不再局限于以前的apache,nginx,tomcat等了。现在的Node.js的模块有很多,能搭建服务器的也有很多,我们这里选择简单粗爆的,搭配是Gulp+Gulp-...
  • 嵌入式Web服务器BOA和CGI编程开发

    千次阅读 2011-09-10 13:11:47
    一 嵌入式WEB服务器常见的有lighttpd,shttpd,thttpd,boa,mathopd,minihttpd,appweb,goahead 二 嵌入式Web服务器BOA的移植方法  随着Internet技术的兴起,在嵌入式设备的管理与交互中,基于Web方式的应用...
  • Flask Web 开发 服务器的启动

    千次阅读 2016-08-04 10:00:32
    Flask 的开发Web 服务器支持很多启动设置选项,但只能在脚本中作为参数传给app.run() 函数。这种方式并不十分方便,传递设置选项的理想方式是使用命令行参数。 Flask-Script 是一个Flask 扩展,为Flask 程序添加了一...
  • web服务器、应用服务器和常见的服务器概念

    千次阅读 多人点赞 2020-04-17 16:36:01
    什么是web服务器,什么是应用服务器? 浏览器的编译原理是什么? http的头文件为何要这样配置? Nginx,Apache等为何要这样操作? 不同的服务器软件有什么作用? 而网上关于http的实战讲解一般都是以操作为主,涉及...
  • Web服务器有哪些(转)

    万次阅读 2019-04-30 16:15:34
    文章目录1、什么是web服务器1.1 Web Service架构和云1.2 Web Service的优势1.3 Web service的发展趋势1.3 本地服务的缺陷2、web服务器有哪些2.1 Apache2.2 IIS2.3 Nginx2.4 Tomcat2.5 Lighttpd2.6 Zeus 1、什么是web...
  • 什么是WEB服务器? 常用的WEB服务器有哪些?   一、什么是WEB服务器  Web服务器可以解析HTTP协议。当Web服务器接收到一个HTTP请求,会返回一个HTTP响应,例如送回一个HTML页面。为了处理一个请求Web服务器可以...
  • 有一个通讯模块是采用的TCP/UDP通讯协议,现在公司要求做一个接口,来实现基于TCP协议的服务器开发,要求接口部署到WEB应用服务器WebLogic中之后就开始对本地某个端口进行监听,获取客户端的请求
  • Java中常用WEB服务器和应用服务器

    万次阅读 2018-06-29 15:20:31
    一、web服务器和应用服务器的区别 Web服务器传送页面使浏览器可以浏览,然而应用程序服务器提供的是客户端应用程序可以调用(call)的方法(methods)。确切一点,你可以说:Web服务器专门处理HTTP请求(request),但是...
  • 使用 Gulp 配置 Web 开发服务器

    千次阅读 2016-09-02 17:54:59
    Web Server 作者:Johanes Schickling 构建工具 Gulp.js 最近正在变得越来越流行。我们可以用它做很多事,比如合并 Javascript 文件或者压缩图片。如果你还不了解 Gulp.js, 可以看一下《前端构建工具 Gulp.js ...
  • 本文是一篇介绍利用MyEclipse进行Web开发的基础教程,通过详细的描述和大量的图片来展现Web开发的具体过程,主要内容如下: a) 创建MyEclipse Web项目; b) 利用JSP向导创建JSP文件; c) 配置Tomcat应用服务器...
  • 今天早上起床,用了360杀毒,杀完毒发现在vs2013写.net代码运行总是报错,错误为无法启动 IIS Express Web 服务器。这哔了狗的错误,一开始以为是项目好久不打开,那个配置不对的问题吗?换了vs2012,vs2017都是报告...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 881,043
精华内容 352,417
关键字:

web服务器开发