自主开发的小型Web服务器

  • 1. 技术特点
  • 2. 具体步骤
  • 3. CGI技术
  • 4. Mysql连接
  • 5. Gitee原码链接
  • 6. 参考Blog

1. 技术特点

  • 网络编程(http协议,TCP/IP协议,socket流式套接字)
  • 多线程技术(线程池)
  • CGI技术
  • Mysql

2. 具体步骤

  • HttpServer.hpp
  1. InitServer()
  2. Start()
  3. 并且将我们的HttpServer设计为单例模式中的懒汉模式(是一个线程安全的单例模式,既要保证安全所以要加锁,但是还要保证效率所以需要双检测
  4. 守护进程(守护进程就是通常讲Daemon进程,是linux后台执行的一种服务进程,特点是独立于控制终端、周期性地执行某种任务或等待处理某些发生事件,不会随终端关闭而停止,直到接受停止信息才会结束,且一般采用以d结尾的名字。)
  • Sock.hpp,对于网络接口的分装
  1. socket()
  2. bind()
  3. listen()
  4. accept()
  5. 线程池技术进行处理所监听到的sock
  6. Getline()一个专门从套接字中一次读取一行的接口,因为Http的request都是以行为分隔符来区分请求行、请求报头、空行、正文。
    补充知识:但是不同的浏览器可能传过来的分隔符是不相同的,有可能是\r、\r\n、\n三种,对于recv的第四个参数flags可以设置为MSG_PEEK,这相当于一个窥探的功能,也就是从内核上把数据读到,但是并没有拷贝到用户缓冲区中
  • Log.hpp
  1. 【日志级别】【message】【时间戳】【filename】【line】
  2. 日志级别:Notice、Warning、Error、fatal4个级别,级别属性依次增加
  3. message:解释
  • Protocol.hpp(最重要)
  1. Entry class 可以进行条件编译的一个类
  2. HttpRequest class 专门处理接收上来的请求文本
  3. HttpResponse class 专门处理准备发送的响应文本
  1. EndPoint class 专门负责通信
  • RecvRequest()
  1. 从socket读取到请求行、请求报头、空行、正文分别设置进HttpRequest class。
  • MakeResponse() 分析并且制作响应
  1. 从请求行需要得到method、url、version,对于本次的method只考虑POST和GET方法,如果不是这两种方法,则会直接返回响应,且错误码为404,对于url如果是GET方法需要区分是否带参数)(考虑到传过来的方式大小写要忽略的问题可以使用strcasecmp()接口,如果相等就返回0,不相等返回-1)

  2. 首先应该得到完整的path,有可能发过来的是一个目录,但是对于任意的一个Web服务器上的目录都有一个默认的index.html(/a/b这个b文件是一个目录,所以首先应该给它拼接上一个/然后再加上index.html),也有可能访问的就是一个普通的html(/a/b/html),还有可能会是访问一个可执行程序(a/b/exe)
    补充知识:

    struct stat {
    mode_t st_mode; // file type & mode(permissions)
    ino_t st_ino; // i-node number(serial number)
    dev_t st_dev; // device number(filesystem)
    dev_t st_rdev; // device number for specials files
    nlink_t st_nlink; // number of links
    uid_t st_uid; // user ID of owner
    gid_t st_gid; // group ID of owner
    off_t st_size; // size in bytes, for regular files
    time_t st_atime; // time of last access
    time_t st_mtime; // time of last modification
    time_t st_ctime; // time of last file status change
    long st_blksize; // best I/O block size
    long st_blocks; -//number of 512-byte blocks allocated
    };

  3. 在GET方法url中有参数,POST方法正文有数据,或者是要取访问一个可执行程序的时候就会触发CGI技术。

  • SendResponse()
  1. 因为已经将制作好的状态行、响应报头设置进了HttpResponse class中,所以可以直接调用所提供的接口进行发送状态行和响应报头
  2. 此时就剩下发送HttpResponse body了,但是需要判断是否CGI模式①GET方法中url有参数②POST方法有正文部分③分析出来的路径最终想要访问的是一个可执行程序。
  3. 判断完是否是CGI模式以后,执行两个不同的函数ExecCgi()或者ExecNonCgi(),对于执行ExecNonCgi()函数来说,目的就是打开一个文件然后通过socket返回(这里使用了一个sendfile()的系统调用接口,可以直接在内核进行两个文件的交互,不需要在先拷贝到用户再从用户拷贝给内核)
    ssize_t sendfile(int out_fd,int in_fd, off_t *offset,size_t count);
    在这里面in_fd should be file descriptor opend for reading
  4. 在处理ExecCgi()函数的时候,创建子进程,然后要把父进程的数据传递给子进程,并且想要这里严格的遵守CGI的传参标准,对于GET方法就是环境变量传参,对于POST因为有可能body很长,所以选择使用管道进行传参。环境变量具有全局属性,所以method可以通过putenv()进行设置,创建子进程,对于这个子进程最大的作用就是要去做程序替换,所以真正意义上是WEB服务器在和CGI程序打交道。这里使用到了进程间通信、进程替换、重定向技术。对于子进程分析方法如果是GET,那么就把query_string通过环境变量传递给CGI,但是对于CGI程序来说,在POST方法下,使用管道进行传参,传递给的只是子进程,CGI程序并拿不到数据,所以这里做了一个约定就是CGI程序可以从标准输入中读取,从标准输出中写入,那么这里就要使用到dup2()重定向接口。
  • Util.hpp
  1. 里面有一个Util class 存放个中方法比如StringToInt()等接口,来使用
  • ThreadPool.hpp
  1. 每监听到一个socket,就把该套接字封装成一个Task,然后塞进任务队列中,然后就是唤醒线程去执行特定的函数,检测到任务队列不为空拿出该任务进行Run()。
  • wwwroot(web根目录)
  1. 每一个目录都有一个默认的index.html
  2. 在W3C上下载了一个静态网页

程序流程解析图

3. CGI技术

CGI(Common Gateway Interface) 是WWW技术中最重要的技术之一,有着不可替代的重要地位。CGI是外部应用程序(CGI程序)与WEB服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的过程。

浏览器除了从服务器下获得资源(网页,图片,文字等),有时候还有能上传一些东西(提交表单,注册用户之类的),看看我们目前的http只能进行获得资源,并不能够进行上传资源,所以目前http并不具有交互式。为了让我们的网站能够实现交互式,我们需要使用CGI完成,时刻记着,我们目前是要写一个http,所以,CGI的所有交互细节,都需要我们来完成。

在我们实现上,要理解CGI,首先的理解GET方法和POST方法的区别:

  • GET方法从浏览器传参数给http服务器时,是需要将参数跟到URI后面的,具体如下:
  • POST方法从浏览器传参数给http服务器时,是需要将参数放的请求正文的。
  • GET方法,如果没有传参,http按照一般的方式进行,返回资源即可
  • GET方法,如果有参数传入,http就需要按照CGI方式处理参数,并将执行结果(期望资源)返回给浏览器
  • POST方法,一般都需要使用CGI方式来进行处理

    需要使用到创建子进程,然后数据还在父进程上,要传给子进程所以使用进程间通信,在程序替换(进程替换),去帮你执行,然后拿到想要的结果在返回给Web服务器,Web服务器在返回给浏览器的过程。

4. Mysql连接

  • 在官网中下载适合自己平台的Mysql connect库(指明include库路径和动态库路径以及要具体链接的动态库中的哪一个库,ldd命令可以查看文件所依赖的动态库,如果还找不到所以要导入一个环境变量export LD_LIBRARY_PATH=/…/…/…)
  • 通过mysql_get_client_info()函数,来验证我们的引入是否成功
  • Mysql接口介绍
  1. 初始化mysql_init(),如:MYSQL *mfp = mysql_init(NULL) 生成一个MYSQL的句柄
  2. 初始化完毕之后,必须先链接数据库,在进行后续操作。
  3. 下发mysql命令mysql_query()
    int mysql_query(MYSQL *mysql, const char *q);
    第一个参数上面已经介绍过,第二个参数为要执行的sql语句,如“select * from table”。(增删改相对简单一些,直接封装成sql语句就好,查询反而还更复杂一些)
  4. 获取执行结果mysql_store_result() ,因为你查询出来的结果还保存在sql的缓冲区当中,所以需要一个这个接口能够从Mysql的缓冲区当中把数据读出来,原型:
    MYSQL_RES *mysql_store_result(MYSQL *mysql);同时该函数会返回MYSQL_RES 这样一个变量,该变量主要用于保存查询的结果。同时该函数malloc了一片内存空间来存储查询过来的数据,所以我们一定要记的 free(result),不然是肯定会造成内存泄漏的。 执行完mysql_store_result以后,其实数据都已经在MYSQL_RES 变量中了,下面的api基本就是读取MYSQL_RES 中的数据。
  5. 获取结果行数mysql_num_rows
    原型:my_ulonglong mysql_num_rows(MYSQL_RES *res);
  6. 获取结果列数mysql_num_fields
    原型:unsigned int mysql_num_fields(MYSQL_RES *res);
  7. 获取列名mysql_fetch_fields
    原型:MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
  8. 获取行结果内容mysql_fetch_row
    原型:MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
  • 简单来说使用方式:比如表单形式提交name和passward,那么CGI程序获得http所发过来的query_string(name和passward),然后进行数据库连接,然后把name和passward封装成一条sql语句然后插入。如果是查询是否存在,那么前面步骤一样,只是封装成一个sql以后去查询,如果在那么验证通过,如果不存在那么就给返回一个注册的页面。但是在本次中只是通过url的类似方式直接显示的访问cgi目录下的sql_cgi的可执行程序。

5. Gitee原码链接

Gitee原码链接: https://gitee.com/meanswer/tiny-web-server

主体框架:自主开发的小型WEB服务器,可以获取静态以及交互式网页的请求与返回,服务器主要使用的是http协议和套接字以及多线程加上系统编程所实现。

6. 参考Blog

  • 开源Tinyhttp原码链接: link.

  • MSG_PEEK详解链接: link.

  • Linux 中的struct stat结构体详解链接: link.

  • gettimeofday()函数详解链接: link.

  • sendfile()函数详解链接: link.

  • CGI获取POST方法和GET方法传参详解链接: link.

  • 管道链接: link.

  • 单例模式链接: link.

  • 线程池链接: link.

  • 守护进程链接: link.

  • 压测工具(无法确定到底是服务器的上限还是带宽的上限)链接: link.

自主开发的小型Web服务器相关推荐

  1. 自主小型Web服务器实现——TinyHttp

    目录 一.功能 二.技术特点 三.主要框架 四.CGI技术 五.流程 六.具体实现细节 6.1 套接字部分 6.2 线程池部分 6.3 协议处理部分(主要部分) 七.测试结果 一.功能 实现一个自主开 ...

  2. mysql 花生壳 2003_基于HTTP协议实现的小型web服务器的方法

    这篇文章主要介绍了基于HTTP协议实现的小型web服务器的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 我们先了解一下这个 ...

  3. 项目--基于http协议的小型web服务器

    在我们对网络的学习过程中,会接触到网络编程,我们在网络中可以深刻认识到服务器与客户端的交互,当我们输入网址时背后发生的一系列后端操作,为了加深我们对网络部分的学习,我们找到了一个开源项目TinyWeb ...

  4. 安卓手机安装php服务器地址,利用Android手机搭建小型Web服务器

    使用背景 手头有一个项目的网页需要跨终端展示,考虑到一些数据的保密性,不能放在公网上的虚拟主机上面,所以需要在局域网中搭建一个小型的Web服务器 现在电子设备更新换代的很快,手里有一些闲置的安卓设备使 ...

  5. Java Web基础入门第八讲 Java Web开发入门——初始WEB服务器

    WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源.Internet上供外界访问的Web资源分为: 静态web资源(如html页面):指we ...

  6. Linux下小型web服务器boa的使用

    boa是一个小型的web服务器,可以用于多种平台,在嵌入式中比较常见. boa的官方网站为www.boa.org,可以在上面下载最新版本的boa:boa-0.94.13.tar.gz (不过这个&qu ...

  7. 迅为IMX6ULL开发板搭建Web服务器(二)

    boa 拷贝到开发板的 bin 目录下 接下来在开发板根目录下建立 www 文件夹,如图 80.15. 然后在开发板上面建立的 www 目录下面建立文件夹 cgi-bin 目录,如图 80.16: 然 ...

  8. arduino 网页服务器,如何将Arduino开发板用作Web服务器

    通过使用以太网扩展板(Ethernet shield),您可以将Arduino开发板用作一个Web服务器. 通过向Arduino开发板配备一个以太网扩展板,您可以将其变成简易的Web服务器,并通过在与 ...

  9. 基于HTTP实现的小型web服务器

    主要流程为:服务器获得请求–>响应并处理请求–>返回结果. 完整的HTTP过渡到TCP实现客户端与服务器的交互过程 1.当客户端执行网络请求的时候,从url中解析出url的主机名,并将主机 ...

最新文章

  1. PYTHON高级全栈开发工程师-老男孩教育
  2. 查看unlix服务器host文件,php代码优化及php相关问题总结
  3. 我的第一份vPlan衍变路线
  4. java中的locksupport_详解Java多线程编程中LockSupport
  5. Modbus网口设备接入多比物联网云平台教程
  6. Controller 如果能保持单例,尽量使用单例
  7. PHP中text里数字相加,excel文字数字如何混合求和
  8. C语言数组学完学啥,我的c语言学习-数组专题
  9. PAT L3-007 天梯地图
  10. 【转】c#数字图像处理(二)彩色图像灰度化,灰度图像二值化
  11. opencv 图像几何变换
  12. # 设置当前标注样式_CAD图纸不会标注?模型空间如何标注,标注样式设置规范解析...
  13. excel文件撤销工作表保护
  14. 关于jxls2.6.0的学习以及遇到的问题(八)
  15. Windows 配置 Aria2教程
  16. 【转载】js 对表格进行各种操作(转)
  17. HTML超大图片加载显示解决方案--图片切割转换成瓦片地图(BaiduMapTileCutter)
  18. 图片合成雾的方法概述
  19. 动态渲染页面的爬取(项目案例:爬取今日头条热点新闻)
  20. 计算机组装与系统安装实验目的,计算机原理与系统组装实验

热门文章

  1. 牛客网-《刷C语言百题》第二期
  2. linux SIGSEGV信号 内存访问错误 Segmentation fault
  3. Windows 下Apache服务器搭建
  4. speedtree中文对照ppt_SpeedTree树木建模入门知识整理
  5. 云计算对电子商务的应用优势
  6. word转出图片(使用免费插件)02
  7. Ubuntu 下Android开发环境搭建
  8. RabbitMQ从入门到实践
  9. 【调剂】苏州科技大学电子与信息工程学院2021年硕士研究生招生第二批调剂信息公告...
  10. Android_学习安卓必备网址