TinywebServer知识点(2)
TinywebServer知识点(2)
原项目地址(点击跳转)
博主添加注释后项目地址(点击跳转)
目前需要进一步补充学习的知识点:
- 数据库
- C++设计模式
一、代码架构
1.项目框架
2.编译运行
在ubuntu下,打开TinyWebServer-master
目录,终端运行
1 | $ sh ./build.ch |
或者直接终端运行
1 | make ./server |
上面两个都可以编译程序代码
终端输入ifconfig
查看服务器本机ip,服务器程序的默认端口号为9006
在浏览器中使用如下,即可访问服务器
1 | ip:Port 如: 192.168.126.128:9006 |
3.服务器运行时流程图
二、HTTP介绍
1.HTTP报文
(1)请求报文
HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据四个部分组成有。两种请求报文
- 请求行,用来说明请求类型(方法),要访问的资源以及所使用的HTTP版本
- 请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息
- 空行,请求头部后面的空行是必须的即使第四部分的请求数据为空,也必须有空行
- 请求数据也叫主体,可以添加任意的其他数据
**GET
**请求报文
1 | GET /562f25980001b1b106000338.jpg HTTP/1.1 |
**POST
**请求报文(注意POST的请求内容不为空)
1 | POST / HTTP1.1 |
GET
与POST
的区别
- 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
- GET请求在URL中传送的参数是有长度限制。(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url
- GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100(指示信息—表示请求已接收,继续处理)continue,浏览器再发送data,服务器响应200 ok(返回数据)
HTTP
多种请求方法:
序号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息,并返回实体主体 |
2 | HEAD | 类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交要处理的数据进行处理请求(例如提交表单或上传文件)。数据包含在请求体中,POST请求可能会导致新的资源的建立和/或已有资源的修改 |
4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容 |
5 | DELETE | 请求服务器删除指定的页面 |
6 | CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
7 | OPTIONS | 允许客户端查看服务器的性能 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
(2)响应报文
响应报文由状态行、消息报头、空行、响应正文四个部分组成
- 状态行,由HTTP协议版本号,状态码,状态消息 三部分组成
- 消息报头,用来说明客户端要使用的一些附加信息
- 空行,消息报头后面的空行是必须的
- 响应正文,服务器返回给客户端的文本信息等
2.HTTP请求/响应步骤
客户端连接到
web
服务器- 一个
HTTP
客户端,通常是浏览器,与Web
服务器的HTTP
端口(默认为80
)建立一个TCP
套接字连接。例如,http://www.baidu.com。(URL)
- 一个
发送
HTTP
请求- 通过
TCP
套接字,客户端向Web
服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4
部分组成
- 通过
服务器接受请求并返回
HTTP
响应Web
服务器解析请求,定位请求资源。服务器将资源复本写到TCP
套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4
部分组成
释放连接
TCP
连接- 若
connection
模式为close
,则服务器主动关闭TCP
连接,客户端被动关闭连接,释放TCP
连接;若connection
模式为keepalive
,则该连接会保持一段时间,在该时间内可以继续接收请求
- 若
客户端浏览器解析
HTML
内容客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应
头告知以下为若干字节的
HTML
文档和文档的字符集。客户端浏览器读取响应数据HTML
,根据HTML
的语法对其进行格式化,并在浏览器窗口中显示
例如:在浏览器地址栏键入URL,按下回车之后会经历以下流程
- 浏览器向
DNS
服务器请求解析该URL
中域名所对应的IP
地址 - 解析出
IP
地址后,根据该IP
地址和默认端口 80,和服务器建立TCP
连接 - 浏览器发出读取文件(
URL
中域名后面部分对应的文件)的HTTP
请求,该请求报文作为TCP
三次握手的第三个报文的数据发送给服务器 - 服务器对浏览器请求作出响应,并把对应的
HTML
文本发送给浏览器 - 释放
TCP
连接 - 浏览器将该
HTML
文本并显示内容
HTTP
协议是基于 TCP/IP
协议之上的应用层协议,基于 请求-响应 的模式。HTTP
协议规定,请求从客户端发出,最后服务器端响应该请求并返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有接收到请求之前不会发送响应
3.项目中HTTP处理步骤
分为三个步骤:
- 连接处理:浏览器端发出http连接请求,主线程创建http对象接收请求并将所有数据读入对应buffer,将该对象插入任务队列,等待工作线程从任务队列中取出一个任务进行处理。
- 处理报文请求:工作线程取出任务后,调用进程处理函数,通过主、从状态机对请求报文进行解析。
- 返回响应报文:解析完之后,生成响应报文,返回给浏览器端
后续会根据代码进行步骤的解析
三、定时器处理非活跃连接
1.原理
如果一个客户端与服务器长时间连接,并且不进行数据的交互,这个连接就没有存在的意义还占据了服务器的资源。在这种情况下,服务器就需要一种手段检测无意义的连接,并对这些连接进行处理
除了处理非活跃的连接之外,服务器还有一些定时事件,比如关闭文件描述符等
为实现这些功能,服务器就需要为各事件分配一个定时器
该项目使用SIGALRM信号来实现定时器,首先每一个定时事件都处于一个升序链表上,通过alarm()函数周期性触发SIGALRM信号,而后信号回调函数利用管道通知主循环,主循环接收到信号之后对升序链表上的定时器进行处理:若一定时间内无数据交换则关闭连接
2.框图
文字描述:
- 服务器首先创建定时器容器链表,然后用统一事件源将异常事件,读写事件和信号事件统一处理,根据不同事件的对应逻辑使用定时器。
- 具体的,浏览器与服务器连接时,创建该连接对应的定时器,并将该定时器添加到定时器容器链表上;
- 处理异常事件时,执行定时事件,服务器关闭连接,从链表上移除对应定时器;
- 处理定时信号时,将定时标志设置为true,以便执行定时器处理函数;
- 处理读/写事件时,若某连接上发生读事件或某连接给浏览器发送数据,将对应定时器向后移动,否则,执行定时事件。
四、日志系统
为了记录服务器的运行状态,错误信息,访问数据的文件等,需要建立一个日志系统。本项目中,使用单例模式(C++设计模式)创建日志系统
这个部分直接结合源码,从log.h入手进行阅读,先查看同步写入的方式,在进行异步写入日志以及阻塞队列的阅读
五、数据库连接池
该项目在处理用户连接时,采用的是:每一个HTTP连接获取一个数据库连接,获取其中的用户账号密码进行对比(有点损耗资源,实际场景下肯定不是这么做的),而后再释放该数据库连接。
那为什么要创建数据库连接池呢?
数据库访问的一般流程为:当系统需要访问数据库时,先系统创建数据库连接,完成数据库操作,然后系统断开数据库连接。——从中可以看出,若系统需要频繁访问数据库,则需要频繁创建和断开数据库连接,而创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。
在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,更加安全可靠。
其实数据库连接池跟线程池的思想基本是一致的。
在该项目中不仅实现了数据库连接池,还将数据库连接的获取与释放通过RAII机制封装,避免手动释放
六、封装同步类
为便于实现同步类的RAII机制,该项目在pthread库的基础上进行了封装,实现了类似于C++11的mutex、condition_variable。
可以阅读文件夹lock中的源码进行这方面的学习