项目简介
源-项目地址: qinguoyi/TinyWebServer: Linux下C++轻量级Web服务器学习 (github.com)
Linux下C++轻量级Web服务器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,助力初学者快速实践网络编程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,搭建属于自己的服务器.
- 使用 线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和模拟Proactor均实现) 的并发模型
- 使用状态机解析HTTP请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,支持解析GET和POST请求
- 访问服务器数据库实现web端用户注册<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、登录功能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以请求服务器图片和视频文件
- 实现同步/异步日志系统<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,记录服务器运行状态
- 经Webbench压力测试可以实现上万的并发连接数据交换
大纲
1. 什么是Web Server<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(网络服务器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)?
一个Web Server就是一个服务器软件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,或者是运行这个服务器软件的硬件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(计算机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。其主要功能是通过HTTP协议与客户端<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(通常是浏览器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(Browser<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>)进行通信<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,来接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,存储<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,处理来自客户端的HTTP请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并对其请求做出HTTP响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,返回给客户端其请求的内容<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(文件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、网页等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)或返回一个Error信息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。

2. 用户如何与你的Web服务器进行通信?
通常用户使用Web浏览器与相应服务器进行通信<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。在浏览器中键入“域名”或“IP地址:端口号”<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器则先将你的域名解析成相应的IP地址或者直接根据你的IP地址向对应的Web服务器发送一个HTTP请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这一过程首先要通过TCP协议的三次握手建立与目标Web服务器的连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后HTTP协议生成针对目标Web服务器的HTTP请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过TCP<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、IP等协议发送到目标Web服务器上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
3. Web服务器如何接收客户端发来的HTTP请求报文呢?
什么是socket?
Web服务器端通过socket监听来自用户的请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/socket.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><netinet/in.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 创建监听socket文件描述符 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int listenfd = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">socket(PF_INET, SOCK_STREAM, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 创建监听socket的TCP/IP的IPV4 socket地址 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">sockaddr_in address; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">bzero(&address, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">sizeof(address)); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">address.sin_family = AF_INET; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">address.sin_addr.s_addr = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">htonl(INADDR_ANY); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* INADDR_ANY"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">:将套接字绑定到所有可用的接口 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">address.sin_port = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">htons(port); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int flag = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* SO_REUSEADDR 允许端口被重复使用 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">sizeof(flag)); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 绑定socket和它的地址 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">ret = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">bind(listenfd, ("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct sockaddr*)&address, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">sizeof(address)); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 创建监听队列以存放待处理的客户连接"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">,在这些客户连接被accept()之前 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">ret = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">listen(listenfd, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5);
|
远端的很多用户会尝试去connect()这个Web Server上正在listen的这个port<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而监听到的这些连接会排队等待被accept()<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。由于用户连接请求是随机到达的异步事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每当监听socket<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(listenfd<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)listen到新的客户连接并且放入监听队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们都需要告诉我们的Web服务器有连接来了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,accept这个连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并分配一个逻辑单元来处理这个用户请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
什么是EPOLL I/O复用?
参考: 浅谈Linux IO复用 - 知乎 (zhihu.com)
IO复用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(I/O Multiplexing<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)通俗的来说<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:是同时处理多个描述符IO事件的一种技术手段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这些文件描述符包括<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:socket套接字<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、普通文件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、设备文件等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
举个简单例子<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:tcp server同时处理两个文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个是标准输入<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个是tcp连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。当server接收标准输入时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可能会因调用fgets()而阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从而无法及时处理另一个tcp连接的可读事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,比如有tcp client发送了数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
如果tcp server想要同时处理多个描述符的事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可能的做法是开启多个线程或进程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,各自等待描述符的可读可写事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但这样一来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,就需要引入线程间同步和通信问题<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,大大增加编程的复杂性<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
但IO复用技术的出现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以很好的解决上述问题<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它直接管理多个描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,选出IO事件ready的描述符列表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这种操作方式允许应用程序以较低的成本<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、较高的效率<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时管理多个描述符的IO事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
我们在处理用户请求的同时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,还需要继续监听其他客户的请求并分配其另一逻辑单元来处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(并发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时处理多个事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,后面会提到使用线程池实现并发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。这里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器通过epoll这种I/O复用技术<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(还有select和poll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)来实现对监听socket<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(listenfd<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)和连接socket<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(客户请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)的同时监听<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。 注意I/O复用虽然可以同时监听多个文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但是它本身是阻塞的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并且当有多个文件描述符同时就绪的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果不采取额外措施<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,程序则只能按顺序处理其中就绪的每一个文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以为提高效率<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们将在这部分通过线程池来实现并发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(多线程并发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,为每个就绪的文件描述符分配一个逻辑单元<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)来处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
服务器程序通常需要处理三类事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:I/O事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号及定时事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。有两种事件处理模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
- Reactor模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:要求主线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(I/O处理单元<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)只负责监听文件描述符上是否有事件发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、可写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,若有<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则立即通知工作线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(逻辑单元<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,将socket可读可写事件放入请求队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,交给工作线程处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- Proactor模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:将所有的I/O操作都交给主线程和内核来处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(进行读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,工作线程仅负责处理逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如主线程读完成后
users[sockfd].read()<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,选择一个工作线程来处理客户请求pool->append(users + sockfd)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
通常使用同步I/O模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(如epoll_wait<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)实现Reactor<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用异步I/O<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(如aio_read和aio_write<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)实现Proactor<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。在此项目中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们使用的是同步I/O模拟的Proactor事件处理模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
Linux下有三种IO复用方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:epoll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,select和poll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为什么用epoll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它和其他两个有什么区别呢<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>?
- 对于select和poll来说<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所有文件描述符都是在用户态被加入其文件描述符集合的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每次调用都需要将整个集合拷贝到内核态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;epoll则将整个文件描述符集合维护在内核态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每次添加文件描述符的时候都需要执行一个系统调用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。系统调用的开销是很大的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而且在有很多短期活跃连接的情况下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,epoll可能会慢于select和poll由于这些大量的系统调用开销<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- select使用线性表描述文件描述符集合<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,文件描述符有上限<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;poll使用链表来描述<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;epoll底层通过红黑树来描述<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并且维护一个ready list<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将事件表中已经就绪的事件添加到这里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在使用epoll_wait调用时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅观察这个list中有没有数据即可<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- select和poll的最大开销来自内核判断是否有文件描述符就绪这一过程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:每次执行select或poll调用时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它们会采用遍历的方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,遍历整个文件描述符集合去判断各个文件描述符是否有活动<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;epoll则不需要去以这种方式检查<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当有活动产生时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,会自动触发epoll回调函数通知epoll文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后内核将这些就绪的文件描述符放到之前提到的ready list中等待epoll_wait调用后被处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- select和poll都只能工作在相对低效的LT模式下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而epoll同时支持LT和ET模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- 综上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当监测的fd数量较小<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,且各个fd都很活跃的情况下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,建议使用select和poll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;当监听的fd数量较多<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,且单位时间仅部分fd活跃的情况下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用epoll会明显提升性能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
从工作原理中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也可以看出select与epoll的诸多不同<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
优缺点对比如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
- 支持监听的描述符数量
- select支持的描述符数量受限<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:因为需要遍历描述符列表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、多次用户空间到内核空间的内存拷贝<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- epoll可以支撑监听海量的描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
性能
- select随着描述符数量的增多<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,性能下降严重<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,时间复杂度 > O(n)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- epoll通过回调函数的方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在描述符增长的情况下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,依旧有出色的性能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
使用场景
- select使用方式较为简单<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,适用于描述符量级小的情况<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- epoll适用于同时监听大量的描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
Epoll对文件操作符的操作有两种模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:LT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(电平触发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)和ET<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(边缘触发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,二者的区别?
- LT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(水平触发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,默认<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>:当时间发生时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果用户不处理的话<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核会一直通知此事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- ET<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(垂直触发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>:只有当事件状态发生改变时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核才会通知<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。(如果描述符缓冲区还有未处理的数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核下次不会再通知了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因为状态没有发生改变<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
4. Web服务器如何处理以及响应接收到的HTTP请求报文呢?
参考: C++ 线程池 - 敬方的个人博客 | BY Blog (wangpengcheng.github.io)
该项目使用线程池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(半同步半反应堆模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)并发处理用户请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程负责读写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,工作线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(线程池中的线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)负责处理逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(HTTP请求报文的解析等等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。 通过之前的代码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们将listenfd上到达的connection通过 accept()接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并返回一个新的socket文件描述符connfd用于和用户通信<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并对用户请求返回响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时将这个connfd注册到内核事件表中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,等用户发来请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
什么是线程池?
这个过程是<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:通过epoll_wait发现这个connfd上有可读事件了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(EPOLLIN<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,主线程就将这个HTTP的请求报文读进这个连接socket的读缓存中users[sockfd].read()<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后将该任务对象<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)插入线程池的请求队列中pool->append(users + sockfd);<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,线程池的实现还需要依靠锁机制以及信号量机制来实现线程同步<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,保证操作的原子性<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- 所谓线程池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,就是一个
pthread_t类型的普通数组<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过pthread_create()函数创建m_thread_number个线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用来执行worker()函数以执行每个请求处理函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(HTTP请求的process函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,通过pthread_detach()将线程设置成脱离态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(detached<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当这一线程运行结束时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它的资源会被系统自动回收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而不再需要在其它线程中对其进行 pthread_join() 操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- 操作工作队列一定要加锁<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(
locker<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,因为它被所有线程共享<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- 我们用信号量来标识请求队列中的请求数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过
m_queuestat.wait();来等待一个请求队列中待处理的HTTP请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后交给线程池中的空闲线程来处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为什么要使用线程池?
当你需要限制你应用程序中同时运行的线程数时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,线程池非常有用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。因为启动一个新线程会带来性能开销<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每个线程也会为其堆栈分配一些内存等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。为了任务的并发执行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们可以将这些任务任务传递到线程池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而不是为每个任务动态开启一个新的线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
关于HTTP请求?
本项目中的HTTP请求的入口函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">http_conn::process"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">() { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> HTTP_CODE read_ret = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">process_read(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">if(read_ret == NO_REQUEST) { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">modfd(m_epollfd, m_sockfd, EPOLLIN); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">return; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> } "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">bool write_ret = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">process_write(read_ret); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">if(!write_ret) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">close_conn(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">modfd(m_epollfd, m_sockfd, EPOLLOUT); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">}
|
HTTP请求报文由请求行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(request line<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>、请求头部<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(header<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>、空行和请求数据四个部分组成有<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。两种请求报文
GET<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(Example<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight awk"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">GET "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/562f25980001b1b106000338.jpg HTTP/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1.1 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Host:img.mukewang.com "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">User-Agent:Mozilla/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5.0 (Windows NT "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">10.0; WOW64) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">AppleWebKit"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/537.36 (KHTML, like Gecko) Chrome/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">51.0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2704.106 Safari/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">537.36 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Accept:image"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/webp,image/*,*/*;q="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0.8 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Referer:[http:"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//www.imooc.com"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/](http:/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"regexp"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/www.imooc.com/) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Accept-Encoding:gzip, deflate, sdch "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Accept-Language:zh-CN,zh;q="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0.8 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">空行 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">请求数据为空
|
POST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(Example<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,注意POST的请求内容不为空<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight ruby"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">POST / "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">HTTP1."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Host"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">:www.wrox.com "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">User-"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Agent"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">:Mozilla/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">4.0 (compatible; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">MSIE "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">6.0; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Windows "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NT "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5.1; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">SV1; ."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NET "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2.0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">50727; ."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NET "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3.0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">04506."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">648; ."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NET "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"variable constant_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3.5."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">21022) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Content-"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">:application/x-www-form-urlencoded "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Content-"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Length"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">:"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">40 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Connection: "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Keep-"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Alive "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">空行 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">name="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Professional%20Ajax&publisher="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">Wiley
|
GET和POST的区别
- 最直观的区别就是GET把参数包含在URL中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,POST通过request body传递参数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- GET请求参数会被完整保留在浏览器历史记录里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而POST中的参数不会被保留<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- GET请求在URL中传送的参数是有长度限制<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。(大多数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)浏览器通常都会限制url长度在2K个字节<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(大多数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)服务器最多处理64K大小的url<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- GET产生一个TCP数据包<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;POST产生两个TCP数据包<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。对于GET方式的请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器会把http header和data一并发送出去<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器响应200<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(返回数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>;而对于POST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器先发送header<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器响应100<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(指示信息—表示请求已接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,继续处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)continue<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器再发送data<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器响应200 ok<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(返回数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。
process_read()函数的作用就是将类似上述例子的请求报文进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因为用户的请求内容包含在这个请求报文里面<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,只有通过解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,知道用户请求的内容是什么<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是请求图片<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,还是视频<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,或是其他请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们根据这些请求返回相应的HTML页面等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。项目中使用主从状态机的模式进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从状态机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(parse_line<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)负责读取报文的一行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主状态机负责对该行数据进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主状态机内部调用从状态机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从状态机驱动主状态机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。每解析一部分都会将整个请求的m_check_state状态改变<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,状态机也就是根据这个状态来进行不同部分的解析跳转的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
parse_request_line(text)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析请求行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也就是GET中的GET /562f25980001b1b106000338.jpg HTTP/1.1这一行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,或者POST中的POST / HTTP1.1这一行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。通过请求行的解析我们可以判断该HTTP请求的类型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(GET/POST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,而请求行中最重要的部分就是URL部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们会将这部分保存下来用于后面的生成HTTP响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
parse_headers(text);<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析请求头部<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,GET和POST中空行以上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,请求行以下的部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
parse_content(text);<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析请求数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对于GET来说这部分是空的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因为这部分内容已经以明文的方式包含在了请求行中的URL部分了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;只有POST的这部分是有数据的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,项目中的这部分数据为用户名和密码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们会根据这部分内容做登录和校验<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并涉及到与数据库的连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
5. 数据库连接池是如何运行的?
在处理用户注册<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,登录请求的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们需要将这些用户的用户名和密码保存下来用于新用户的注册及老用户的登录校验<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这种功能是服务器端通过用户键入的用户名密码和数据库中已记录下来的用户名密码数据进行校验实现的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。若每次用户请求我们都需要新建一个数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,请求结束后我们释放该数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当用户请求连接过多时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这种做法过于低效<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以类似线程池的做法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们构建一个数据库连接池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,预先生成一些数据库连接放在那里供用户请求使用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
我们首先看单个数据库连接是如何生成的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
- 使用
mysql_init()初始化连接
- 使用
mysql_real_connect()建立一个到mysql数据库的连接
- 使用
mysql_query()执行查询语句
- 使用
result = mysql_store_result(mysql)获取结果集
- 使用
mysql_num_fields(result)获取查询的列数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,mysql_num_rows(result)获取结果集的行数
- 通过
mysql_fetch_row(result)不断获取下一行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后循环输出
- 使用
mysql_free_result(result)释放结果集所占内存
- 使用
mysql_close(conn)关闭连接
对于一个数据库连接池来讲<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,就是预先生成多个这样的数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后放在一个链表中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时维护最大连接数MAX_CONN<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当前可用连接数FREE_CONN和当前已用连接数CUR_CONN这三个变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。同样注意在对连接池操作时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(获取<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,释放<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,要用到锁机制<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因为它被所有线程共享<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
6. 什么是CGI校验?
OK<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,弄清楚了数据库连接池的概念及实现方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们继续回到第4部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对用户的登录及注册等POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器是如何做校验的
CGI<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(通用网关接口<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,它是一个运行在Web服务器上的程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在编译的时候将相应的.cpp文件编程成.cgi文件并在主程序中调用即可<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(通过社长的makefile文件内容也可以看出<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。这些CGI程序通常通过客户在其浏览器上点击一个button时运行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这些程序通常用来执行一些信息搜索<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、存储等任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而且通常会生成一个动态的HTML网页来响应客户的HTTP请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。我们可以发现项目中的sign.cpp文件就是我们的CGI程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将用户请求中的用户名和密码保存在一个id_passwd.txt文件中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过将数据库中的用户名和密码存到一个map中用于校验<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。在主程序中通过execl(m_real_file, &flag, name, password, NULL);这句命令来执行这个CGI文件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里CGI程序仅用于校验<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并未直接返回给用户响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这个CGI程序的运行通过多进程来实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,根据其返回结果判断校验结果<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(使用pipe进行父子进程的通信<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,子进程将校验结果写到pipe的写端<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,父进程在读端读取<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。
7. 如何生成HTTP响应并返回给用户?
通过以上操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们已经对读到的请求做好了处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后也对目标文件的属性作了分析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若目标文件存在<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、对所有用户可读且不是目录时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则使用mmap将其映射到内存地址m_file_address处<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并告诉调用者获取文件成功FILE_REQUEST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。 接下来要做的就是根据读取结果对用户做出响应了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也就是到了process_write(read_ret);这一步<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该函数根据process_read()的返回结果来判断应该返回给用户什么响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们最常见的就是404错误了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,说明客户请求的文件不存在<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,除此之外还有其他类型的请求出错的响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体的可以去百度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。然后呢<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,假设用户请求的文件存在<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而且已经被mmap到m_file_address这里了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,那么我们就将做如下写操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将响应写到这个connfd的写缓存m_write_buf中去
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">case FILE_REQUEST: { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">add_status_line("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">200, ok_200_title); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">if(m_file_stat.st_size != "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0) { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">add_headers(m_file_stat.st_size); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> m_iv["bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0].iov_base = m_write_buf; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> m_iv["bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0].iov_len = m_write_idx; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> m_iv["bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1].iov_base = m_file_address; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> m_iv["bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1].iov_len = m_file_stat.st_size; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> m_iv_count = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> bytes_to_send = m_write_idx + m_file_stat.st_size; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">return "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"literal"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">true; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> } "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">else { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">const "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">char* ok_string = "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"<html><body></body></html>"; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">add_headers("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">strlen(ok_string)); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">if(!"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">add_content(ok_string)) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">return "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"literal"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">false; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> } "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">}
|
00 Linux网络开发环境搭建
MacOS虽然也是UNIX操作系统, 但本项目中的许多库函数在Mac下并未得到支持, 所以本次项目使用Docker下的虚拟环境进行开发.
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight sh"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#macos "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">docker pull centos "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">docker -it run centos "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#centos container "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">yum update -y "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">yum install gcc-c++ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">yum install mysql "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">yum install mysql-server "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">ln -s /usr/lib64/libmysqlclient.so /usr/lib/libmysqlclient.so
|
01 线程同步机制封装类
C++的RSII机制
- RAII全称是“Resource Acquisition is Initialization”<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直译过来是“资源获取即初始化”.
- 在构造函数中申请分配资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在析构函数中释放资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。因为C++的语言机制保证了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当一个对象创建的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,自动调用构造函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当对象超出作用域的时候会自动调用析构函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。所以<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在RAII的指导下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们应该使用类来管理资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将资源和对象的生命周期绑定
- RAII的核心思想是将资源或者状态与对象的生命周期绑定<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过C++的语言机制<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,实现资源和状态的安全管理,智能指针是RAII最好的例子
Linux中的条件变量
linux中的条件变量的使用-CSDN博客
条件变量提供了一种线程间的通知机制,当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.
pthread_cond_init函数用于初始化条件变量
pthread_cond_destory函数销毁条件变量
pthread_cond_broadcast函数以广播的方式唤醒所有等待目标条件变量的线程
pthread_cond_wait函数用于等待目标条件变量.该函数调用时需要传入 mutex参数(加锁的互斥锁) ,函数执行时,先把调用线程放入条件变量的请求队列,然后将互斥锁mutex解锁,当函数成功返回为0时,互斥锁会再次被锁上. 也就是说函数内部会有一次解锁和加锁操作.
Linux中的互斥量
Linux线程-互斥锁-CSDN博客
互斥锁,也成互斥量,可以保护关键代码段,以确保独占式访问.当进入关键代码段,获得互斥锁将其加锁;离开关键代码段,唤醒等待该互斥锁的线程.
pthread_mutex_init函数用于初始化互斥锁
pthread_mutex_destory函数用于销毁互斥锁
pthread_mutex_lock函数以原子操作方式给互斥锁加锁
pthread_mutex_unlock函数以原子操作方式给互斥锁解锁
以上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,成功返回0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,失败返回errno
Linux中的信号量
信号量是一种特殊的变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它只能取自然数值并且只支持两种操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:等待(P)和信号(V).假设有信号量SV<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对其的P<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、V操作如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
- P<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果SV的值大于0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将其减一<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;若SV的值为0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则挂起执行
- V<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果有其他进行因为等待SV而挂起<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则唤醒<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;若没有<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将SV值加一
信号量的取值可以是任何自然数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最常用的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最简单的信号量是二进制信号量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,只有0和1两个值.
sem_init函数用于初始化一个未命名的信号量
sem_destory函数用于销毁信号量
sem_wait函数将以原子操作方式将信号量减一,信号量为0时,sem_wait阻塞
sem_post函数以原子操作方式将信号量加一,信号量大于0时,唤醒调用sem_post的线程
以上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,成功返回0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,失败返回errno
02 数据库连接池
什么是数据库连接池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>?
池是一组资源的集合<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这组资源在服务器启动之初就被完全创建好并初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。通俗来说<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,池是资源的容器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,本质上是对资源的复用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
顾名思义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,连接池中的资源为一组数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由程序动态地对池中的连接进行使用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,释放<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
当系统开始处理客户请求的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果它需要相关的资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以直接从池中获取<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,无需动态分配<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;当服务器处理完一个客户连接后,可以把相关的资源放回池中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,无需执行系统调用释放资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
数据库访问的一般流程是什么<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>?
当系统需要访问数据库时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,先系统创建数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,完成数据库操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后系统断开数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为什么要创建连接池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>?
从一般流程中可以看出<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若系统需要频繁访问数据库<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则需要频繁创建和断开数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而创建数据库连接是一个很耗时的操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也容易对数据库造成安全隐患<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
在程序初始化的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,集中创建多个数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并把他们集中管理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,供程序使用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以保证较快的数据库读写速度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,更加安全可靠<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
数据库连接池
池可以看做资源的容器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以多种实现方法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,比如数组<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、链表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、队列等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。这里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用单例模式和链表创建数据库连接池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,实现对数据库连接资源的复用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
项目中的数据库模块分为两部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其一是数据库连接池的定义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其二是利用连接池完成登录和注册的校验功能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,工作线程从数据库连接池取得一个连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,访问数据库中的数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,访问完毕后将连接交还连接池<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
单例模式创建<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码描述连接池的单例实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
连接池代码实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对连接池的外部访问接口进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
RAII机制释放数据库连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述连接释放的封装逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
数据库连接池
- 单例模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,保证唯一
- list实现连接池
- 连接池为静态大小
- 互斥锁实现线程安全
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">class "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">connectionRAII{ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">public: "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">connectionRAII(MYSQL **con, connection_pool *connPool); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> ~"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">connectionRAII(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">private: "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> MYSQL *conRAII; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> connection_pool *poolRAII; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">};
|
目的是实现connction的自动连接与释放, 以下显示了connectionRAII在用户代码是如何被使用, 起到了怎样的效果
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//引入RAII类前: 用户代码中需要手动获得和释放连接 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//从连接池中取出一个数据库连接 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">request->mysql = m_connPool->"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">GetConnection(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//process(模板类中的方法,这里是http类)进行处理 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">request->"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">process(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//将数据库连接放回连接池 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">m_connPool->"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">ReleaseConnection(request->mysql); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//引入RAII类之后: 连接不使用可以被自动释放 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">connectionRAII "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">mysqlcon"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">(&request->mysql, m_connPool); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">request->"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">process();
|
03 半同步半反应堆线程池
服务器编程基本框架

主要由I/O单元<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,逻辑单元和网络存储单元组成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中每个单元之间通过请求队列进行通信<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从而协同完成任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
其中I/O单元用于处理客户端连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,读写网络数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;逻辑单元用于处理业务逻辑的线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;网络存储单元指本地数据库和文件等<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
五种I/O模型
阻塞IO:调用者调用了某个函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,等待这个函数返回<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,期间什么也不做<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不停的去检查这个函数有没有返回<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,必须等这个函数返回才能进行下一步动作
非阻塞IO:非阻塞等待<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每隔一段时间就去检测IO事件是否就绪<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。没有就绪就可以做其他事<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。非阻塞I/O执行系统调用总是立即返回<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不管时间是否已经发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若时间没有发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则返回-1<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,此时可以根据errno区分这两种情况<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对于accept<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,recv和send<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,事件未发生时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,errno通常被设置成eagain
信号驱动IO:linux用套接口进行信号驱动IO<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,安装一个信号处理函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进程继续运行并不阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当IO时间就绪<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进程收到SIGIO信号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。然后处理IO事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
IO复用:linux用select/poll函数实现IO复用模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这两个函数也会使进程阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但是和阻塞IO所不同的是这两个函数可以同时阻塞多个IO操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。而且可以同时对多个读操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、写操作的IO函数进行检测<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。知道有数据可读或可写时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,才真正调用IO操作函数
异步IO:linux中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、文件偏移及通知的方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后立即返回<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当内核将数据拷贝到缓冲区后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,再通知应用程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
事件处理模式
reactor模式中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,有的话立即通知工作线程(逻辑单元 )<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,读写数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、接受新连接及处理客户请求均在工作线程中完成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。通常由同步I/O实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
proactor模式中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程和内核负责处理读写数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、接受新连接等I/O操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,工作线程仅负责业务逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如处理客户请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。通常由异步I/O实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
同步I/O模拟proactor模式
由于异步I/O并不成熟<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,实际中使用较少<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里将使用同步I/O模拟实现proactor模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
同步I/O模型的工作流程如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(epoll_wait为例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>:
主线程往epoll内核事件表注册socket上的读就绪事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
主线程调用epoll_wait等待socket上有数据可读
当socket上有数据可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,epoll_wait通知主线程,主线程从socket循环读取数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到没有更多数据可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后将读取到的数据封装成一个请求对象并插入请求队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
睡眠在请求队列上某个工作线程被唤醒<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它获得请求对象并处理客户请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后往epoll内核事件表中注册该socket上的写就绪事件
主线程调用epoll_wait等待socket可写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
当socket上有数据可写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,epoll_wait通知主线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。主线程往socket上写入服务器处理客户请求的结果<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
并发编程模式
并发编程方法的实现有多线程和多进程两种<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但这里涉及的并发模式指I/O处理单元与逻辑单元的协同完成任务的方法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
半同步/半反应堆并发模式是半同步/半异步的变体<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将半异步具体化为某种事件处理模式.
并发模式中的同步和异步
同步指的是程序完全按照代码序列的顺序执行
异步指的是程序的执行需要由系统事件驱动
半同步/半异步模式工作流程
半同步/半反应堆工作流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(以Proactor模式为例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
主线程充当异步线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,负责监听所有socket上的事件
若有新请求到来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程接收之以得到新的连接socket<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后往epoll内核事件表中注册该socket上的读写事件
如果连接socket上有读写事件发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程从socket上接收数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并将数据封装成请求对象插入到请求队列中
所有工作线程(线程池)睡眠在请求队列上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当有任务到来时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过竞争<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(如互斥锁<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)获得任务的接管权
线程池
空间换时间,牺牲服务器的硬件资源,换取运行效率.
池是一组资源的集合,这组资源在服务器启动之初就被完全创建好并初始化,这称为静态资源.
当服务器进入正式运行阶段,开始处理客户请求的时候,如果它需要相关的资源,可以直接从池中获取,无需动态分配.
当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用释放资源.
静态成员变量
将类成员变量声明为static<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则为静态成员变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,与一般的成员变量不同<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,无论建立多少对象<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,都只有一个静态成员变量的拷贝<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,静态成员变量属于一个类<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所有对象共享<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态变量在编译阶段就分配了空间<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对象还没创建时就已经分配了空间<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,放到全局静态区<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
最好是类内声明<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,类外初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(以免类名访问静态成员访问不到<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>。
无论公有<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,私有<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,静态成员都可以在类外定义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但私有成员仍有访问权限<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
非静态成员类外不能初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态成员数据是共享的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态成员函数
将类成员函数声明为static<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则为静态成员函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态成员函数可以直接访问静态成员变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不能直接访问普通成员变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但可以通过参数传递的方式访问<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
普通成员函数可以访问普通成员变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也可以访问静态成员变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态成员函数没有this指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。非静态数据成员为对象单独维护<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但静态成员函数为共享函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,无法区分是哪个对象<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因此不能直接访问普通变量成员<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也没有this指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
pthread_create陷阱
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">static "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">worker"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *args); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pthread_create(m_threads[i], "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"literal"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NULL, worker, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">this)!="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0
|
首先看一下该函数的函数原型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
pthread_create()函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:创建线程 (biancheng.net)
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pthread_create"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pthread_t *thread, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">const "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pthread_attr_t *attr, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *(*start_routine) ("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *), "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *arg);
|
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pthread_create(m_threads[i], "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"literal"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">NULL, worker, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">this)!="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//本质上工作线程的内容还是threadpool<T>::run() "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">template <"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">typename T> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *threadpool<T>::"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">worker("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *arg) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">{ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">threadpool *pool = (threadpool *)arg; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">pool->"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">run(); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">return pool; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">}
|
函数原型中的第三个参数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为函数指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,指向处理线程函数的地址<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。该函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,要求为静态函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。如果处理线程函数为类成员函数时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要将其设置为静态成员函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
pthread_create的函数原型中第三个参数的类型为函数指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,指向的线程处理函数参数类型为(void *),若线程函数为类成员函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则this指针会作为默认的参数被传进函数中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从而和线程函数参数(void*)不能匹配<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不能通过编译<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
静态成员函数就没有这个问题<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,里面没有this指针<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
线程池设计
使用一个工作队列完全解除了主线程和工作线程的耦合关系<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:主线程往工作队列中插入任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,工作线程通过竞争来取得任务并执行它<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
- 同步I/O模拟proactor模式
- 半同步/半反应堆
- 线程池
线程池的设计模式为半同步/半反应堆<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中反应堆具体为Proactor事件处理模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主线程为异步线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,负责监听文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,接收socket新连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若当前监听的socket发生了读写事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后将任务插入到请求队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。工作线程从请求队列中取出任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,完成读写数据的处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
04 HTTP连接处理
IO复用
一文看懂IO多路复用 - 知乎 (zhihu.com)
<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>《Linux高性能服务器编程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>》第九章 I/O复用-CSDN博客
epoll涉及的知识较多<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里仅对API和基础知识作介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。更多资料请查阅资料<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,或查阅游双的Linux高性能服务器编程 第9章 I/O复用
epoll_create函数
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/epoll.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">epoll_create"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int size)
|
创建一个指示epoll内核事件表的文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该描述符将用作其他epoll系统调用的第一个参数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,size不起作用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
epoll_ctl函数
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/epoll.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">epoll_ctl"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int epfd, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int op, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int fd, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct epoll_event *event)
|
该函数用于操作内核事件表监控的文件描述符上的事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:注册<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、修改<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、删除
epfd<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:为epoll_creat的句柄
op<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示动作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用3个宏来表示<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
EPOLL_CTL_ADD (注册新的fd到epfd)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
EPOLL_CTL_MOD (修改已经注册的fd的监听事件)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
EPOLL_CTL_DEL (从epfd删除一个fd)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;
event<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:告诉内核需要监听的事件
上述event是epoll_event结构体指针类型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,表示内核所监听的事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体定义如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">epoll_event { "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">__uint32_t events; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* Epoll events */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">epoll_data_t data; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* User data variable */};
|
events描述事件类型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中epoll事件类型有以下几种
EPOLLIN<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示对应的文件描述符可以读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(包括对端SOCKET正常关闭<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
EPOLLOUT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示对应的文件描述符可以写
EPOLLPRI<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示对应的文件描述符有紧急的数据可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(这里应该表示有带外数据到来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
EPOLLERR<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示对应的文件描述符发生错误
EPOLLHUP<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:表示对应的文件描述符被挂断<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;
EPOLLET<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:将EPOLL设为边缘触发(Edge Triggered)模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这是相对于水平触发(Level Triggered)而言的
EPOLLONESHOT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:只监听一次事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当监听完这次事件之后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果还需要继续监听这个socket的话<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要再次把这个socket加入到EPOLL队列里
epoll_wait函数
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/epoll.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">epoll_wait"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int epfd, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct epoll_event *events, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int maxevents, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int timeout)
|
该函数用于等待所监控文件描述符上有事件的产生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,返回就绪的文件描述符个数
events<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:用来存内核得到事件的集合<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
maxevents<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:告之内核这个events有多大<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这个maxevents的值不能大于创建epoll_create()时的size<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
timeout<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:是超时时间
-1<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:阻塞
0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:立即返回<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,非阻塞
0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:指定毫秒
返回值<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:成功返回有多少文件描述符就绪<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,时间到时返回0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,出错返回-1
select/poll/epoll
调用函数
select和poll都是一个函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,epoll是一组函数
文件描述符数量
select通过线性表描述文件描述符集合<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,文件描述符有上限<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一般是1024<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但可以修改源码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,重新编译内核<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不推荐
poll是链表描述<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,突破了文件描述符上限<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最大可以打开文件的数目
epoll通过红黑树描述<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最大可以打开文件的数目<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以通过命令ulimit -n number修改<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅对当前终端有效
将文件描述符从用户传给内核
select和poll通过将所有文件描述符拷贝到内核态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每次调用都需要拷贝
epoll通过epoll_create建立一棵红黑树<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过epoll_ctl将要监听的文件描述符注册到红黑树上
内核判断就绪的文件描述符
select和poll通过遍历文件描述符集合<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,判断哪个文件描述符上有事件发生
epoll_create时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核除了帮我们在epoll文件系统里建了个红黑树用于存储以后epoll_ctl传来的fd外<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,还会再建立一个list链表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用于存储准备就绪的事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当epoll_wait调用时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅仅观察这个list链表里有没有数据即可<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
epoll是根据每个fd上面的回调函数(中断函数)判断<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,只有发生了事件的socket才会主动的去调用 callback函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其他空闲状态socket则不会<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若是就绪事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,插入list
应用程序索引就绪文件描述符
select/poll只返回发生了事件的文件描述符的个数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若知道是哪个发生了事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同样需要遍历
epoll返回的发生了事件的个数和结构体数组<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结构体包含socket的信息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因此直接处理返回的数组即可
工作模式
select和poll都只能工作在相对低效的LT模式下
epoll则可以工作在ET高效模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并且epoll还支持EPOLLONESHOT事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该事件能进一步减少可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、可写和异常事件被触发的次数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
应用场景
当所有的fd都是活跃连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用epoll<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要建立文件系统<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,红黑书和链表对于此来说<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,效率反而不高<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不如selece和poll
当监测的fd数目较小<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,且各个fd都比较活跃<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,建议使用select或者poll
当监测的fd数目非常大<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,成千上万<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,且单位时间只有其中的一部分fd处于就绪状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这个时候使用epoll能够明显提升性能
ET<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、LT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、EPOLLONESHOT
LT水平触发模式
epoll_wait检测到文件描述符有事件发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将其通知给应用程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,应用程序可以不立即处理该事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
当下一次调用epoll_wait时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,epoll_wait还会再次向应用程序报告此事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直至被处理
ET边缘触发模式
epoll_wait检测到文件描述符有事件发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将其通知给应用程序<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,应用程序必须立即处理该事件
必须要一次性将数据读取完<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用非阻塞I/O<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,读取到出现eagain
EPOLLONESHOT
一个线程读取某个socket上的数据后开始处理数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在处理过程中该socket上又有新数据可读<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,此时另一个线程被唤醒读取<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,此时出现两个线程处理同一个socket
我们期望的是一个socket连接在任一时刻都只被一个线程处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过epoll_ctl对该文件描述符注册epolloneshot事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个线程处理socket时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其他线程将无法处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当该线程处理完后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要通过epoll_ctl重置epolloneshot事件
HTTP报文
HTTP报文分为请求报文和响应报文两种<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每种报文必须按照特有格式生成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,才能被浏览器端识别<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
其中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器端向服务器发送的为请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器处理后返回给浏览器端的为响应报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
请求报文
HTTP请求报文由请求行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(request line<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>、请求头部<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(header<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>、空行和请求数据四个部分组成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
其中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,请求分为两种<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,GET和POST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight apache"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 GET /"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">562f25980001b1b106000338.jpg HTTP/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2 Host:img.mukewang.com "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3 User-Agent:Mozilla/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0 (Windows NT "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">10."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0; WOW64) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">4 AppleWebKit/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">537."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">36 (KHTML, like Gecko) Chrome/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">51."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2704."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">106 Safari/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">537."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">36 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5 Accept:image/webp,image/*,*/*;q="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">6 Referer:http://www.imooc.com/ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">7 Accept-Encoding:gzip, deflate, sdch "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 Accept-Language:zh-CN,zh;q="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">9 空行"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">10 请求数据为空
|
- POST
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight apache"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 POST / HTTP1."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2 Host:www.wrox.com "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3 UserAgent:Mozilla/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">4."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0 (compatible; MSIE "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">6."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0; Windows NT "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1; SV1; .NET CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">50727; .NET CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">04506."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">648; .NET CLR "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5."bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">21022) "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">4 Content-Type:application/x-www-form-urlencoded "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5 Content-Length:"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">40 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">6 Connection: Keep-Alive "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">7 空行 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"attribute"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 name=Professional%"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">20Ajax&publisher=Wiley
|
响应报文
HTTP响应也由四个部分组成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,分别是<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:状态行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、消息报头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、空行和响应正文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight basic"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1 HTTP/"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">1.1 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">200 OK "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2 Date: Fri, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">22 May "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">2009 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">06:"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">07:"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">21 GMT "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">3 Content-Type: text/html; charset=UTF-"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"symbol"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">4 空行 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">5<html> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">6<head></head> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">7 <body> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">8 <!--body goes here--> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">9 </body> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">10</html>
|
状态行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由HTTP协议版本号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>, 状态码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>, 状态消息 三部分组成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
第一行为状态行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,(HTTP/1.1<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)表明HTTP版本为1.1版本<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,状态码为200<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,状态消息为OK<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
消息报头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用来说明客户端要使用的一些附加信息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
第二行和第三行为消息报头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,Date:生成响应的日期和时间<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
空行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,消息报头后面的空行是必须的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
响应正文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器返回给客户端的文本信息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。空行后面的html部分为响应正文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
响应状态码
HTTP有5种类型的状态码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
1xx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:指示信息–表示请求已接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,继续处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
2xx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:成功–表示请求正常处理完毕<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
200 OK<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:客户端请求被正常处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
206 Partial content<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:客户端进行了范围请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
3xx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:重定向–要完成请求必须进行更进一步的操作
301 Moved Permanently<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:永久重定向<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该资源已被永久移动到新位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将来任何对该资源的访问都要使用本响应返回的若干个URI之一<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
302 Found<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:临时重定向<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,请求的资源现在临时从不同的URI中获得<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
4xx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:客户端错误–请求有语法错误<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器无法处理请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
400 Bad Request<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:请求报文存在语法错误<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
403 Forbidden<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:请求被服务器拒绝<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
404 Not Found<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:请求不存在<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器上找不到请求的资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
5xx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:服务器端错误–服务器处理请求出错<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
500 Internal Server Error<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:服务器在执行请求时出现错误<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
HTTP请求解析
有限状态机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是一种抽象的理论模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它能够把有限个变量描述的状态变化过程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以可构造可验证的方式呈现出来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。比如<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,封闭的有向图<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
有限状态机可以通过if-else,switch-case和函数指针来实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从软件工程的角度看<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主要是为了封装逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
首先对http报文处理的流程进行简要介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后具体介绍http类的定义和服务器接收http请求的具体过程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
在HTTP报文中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每一行的数据由\r\n作为结束字符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,空行则是仅仅是字符\r\n<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。因此<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以通过查找\r\n将报文拆解成单独的行进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,项目中便是利用了这一点<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
从状态机负责读取buffer中的数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将每行数据末尾的\r\n置为\0\0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并更新从状态机在buffer中读取的位置m_checked_idx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以此来驱动主状态机解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
从状态机从m_read_buf中逐字节读取<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,判断当前字节是否为\r
接下来的字符是\n<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将\r\n修改成\0\0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将m_checked_idx指向下一行的开头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则返回LINE_OK
接下来达到了buffer末尾<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,表示buffer还需要继续接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,返回LINE_OPEN
否则<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,表示语法错误<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,返回LINE_BAD
当前字节不是\r<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,判断是否是\n<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(一般是上次读取到\r就到了buffer末尾<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,没有接收完整<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,再次接收时会出现这种情况<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)
- 如果前一个字符是\r<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将\r\n修改成\0\0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将m_checked_idx指向下一行的开头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则返回LINE_OK
当前字节既不是\r<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也不是\n
- 表示接收不完整<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要继续接收<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,返回LINE_OPEN
主从状态机
流程图和代码分别对状态机和服务器解析请求报文进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
流程图部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述主<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、从状态机调用关系与状态转移过程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
代码部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对http请求报文的解析进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。

三种状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,标识解析位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
CHECK_STATE_REQUESTLINE<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析请求行
CHECK_STATE_HEADER<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析请求头
CHECK_STATE_CONTENT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,解析消息体<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅用于解析POST请求
从状态机
三种状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,标识解析一行的读取状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
LINE_OK<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,完整读取一行
LINE_BAD<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,报文语法有误
LINE_OPEN<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,读取的行不完整
在http请求接收部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,会涉及到init和read_once函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但init仅仅是对私有成员变量进行初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不用过多讲解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
这里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对read_once进行介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。read_once读取浏览器端发送来的请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到无数据可读或对方关闭连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,读取到m_read_buffer中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并更新m_read_idx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
HTTP_CODE含义
表示HTTP请求的处理结果<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在头文件中初始化了八种情形<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在报文解析时只涉及到四种<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
NO_REQUEST: 请求不完整<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要继续读取请求报文数据
GET_REQUEST: 获得了完整的HTTP请求
BAD_REQUEST: HTTP请求报文有语法错误
INTERNAL_ERROR: 服务器内部错误<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该结果在主状态机逻辑switch的default下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一般不会触发
解析报文整体流程
process_read通过while循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将主从状态机进行封装<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对报文的每一行进行循环处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
循环条件
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">while((m_check_state==CHECK_STATE_CONTENT && line_status==LINE_OK)||((line_status="bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">parse_line())==LINE_OK))
|
在GET请求报文中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,每一行都是\r\n作为结束<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以对报文进行拆解时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅用从状态机的状态line_status=parse_line())==LINE_OK语句即可<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
但<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在POST请求报文中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,消息体的末尾没有任何字符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以不能使用从状态机的状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里转而使用主状态机的状态作为循环入口条件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
判断条件
主状态机转移到CHECK_STATE_CONTENT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该条件涉及解析消息体
从状态机转移到LINE_OK<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该条件涉及解析请求行和请求头部
两者为或关系<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当条件为真则继续循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,否则退出
循环体
解析完消息体后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,报文的完整解析就完成了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但此时主状态机的状态还是CHECK_STATE_CONTENT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也就是说<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,符合循环入口条件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,还会再次进入循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这并不是我们所希望的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为此<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,增加了该语句<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并在完成消息体解析后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将line_status变量更改为LINE_OPEN<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,此时可以跳出循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,完成报文解析任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
CHECK_STATE_REQUESTLINE
主状态机初始状态是CHECK_STATE_REQUESTLINE<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过调用从状态机来驱动主状态机<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在主状态机进行解析前<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从状态机已经将每一行的末尾\r\n符号改为\0\0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以便于主状态机直接取出对应字符串进行处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
CHECK_STATE_REQUESTLINE
- 主状态机的初始状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,调用parse_request_line函数解析请求行
- 解析函数从m_read_buf中解析HTTP请求行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,获得请求方法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、目标URL及HTTP版本号
- 解析完成后主状态机的状态变为CHECK_STATE_HEADER
解析完请求行后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主状态机继续分析请求头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。在报文中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,请求头和空行的处理使用的同一个函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里通过判断当前的text首位是不是\0字符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若是<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则表示当前处理的是空行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若不是<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则表示当前处理的是请求头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
CHECK_STATE_HEADER
调用parse_headers函数解析请求头部信息
判断是空行还是请求头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若是空行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进而判断content-length是否为0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果不是0<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,表明是POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则状态转移到CHECK_STATE_CONTENT<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,否则说明是GET请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则报文解析结束<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
若解析的是请求头部字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则主要分析connection字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,content-length字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其他字段可以直接跳过<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,各位也可以根据需求继续分析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
若解析的是请求头部字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则主要分析connection字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,content-length字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其他字段可以直接跳过<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,各位也可以根据需求继续分析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。 connection字段判断是keep-alive还是close<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,决定是长连接还是短连接
content-length字段<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里用于读取post请求的消息体长度
如果仅仅是GET请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如项目中的欢迎界面<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,那么主状态机只设置之前的两个状态足矣<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
因为在上篇推文中我们曾说道<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,GET和POST请求报文的区别之一是有无消息体部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,GET请求没有消息体<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当解析完空行之后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,便完成了报文的解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
但后续的登录和注册功能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为了避免将用户名和密码直接暴露在URL中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们在项目中改用了POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将用户名和密码添加在报文中作为消息体进行了封装<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为此<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们需要在解析报文的部分添加解析消息体的模块<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
CHECK_STATE_CONTENT
仅用于解析POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,调用parse_content函数解析消息体
用于保存post请求消息体<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为后面的登录和注册做准备
HTTP响应处理
基础知识:
介绍stat<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、mmap<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、iovec<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、writev
stat
stat函数用于取得指定文件的文件属性<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并将文件属性存储在结构体stat里<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里仅对其中用到的成员进行介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/stat.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//获取文件属性"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">,存储在statbuf中 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">stat"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">const "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">char *pathname, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct stat *statbuf); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">//完整定义很长, 仅介绍用到的 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">stat "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">{ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">mode_t st_mode; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 文件类型和权限 */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">off_t st_size; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* 文件大小"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">,字节数*/ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">}; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"built_in"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">stat(m_real_file, &m_file_stat)<"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"number"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">0 "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">
|
mmap
用于将一个文件或其他对象映射到内存<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,提高文件的访问速度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void* "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">mmap"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void* start,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">size_t length,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int prot,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int flags,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int fd,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">off_t offset); "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">munmap"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void* start,"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">size_t length);
|
start<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:映射区的开始地址<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,设置为0时表示由系统决定映射区的起始地址
length<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:映射区的长度
prot<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:期望的内存保护标志<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不能与文件的打开模式冲突
flags<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:指定映射对象的类型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,映射选项和映射页是否可以共享
- MAP_PRIVATE 建立一个写入时拷贝的私有映射<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内存区域的写入不会影响到原文件
fd<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:有效的文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一般是由open()函数返回
off_toffset<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:被映射对象内容的起点
iovec
定义了一个向量元素<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通常<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这个结构用作一个多元素的数组<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title class_"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">iovec "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">{ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">void *iov_base; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* Pointer to data. */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">size_t iov_len; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"comment"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">/* Length of data. */ "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">}; "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">
|
- iov_base指向数据的地址
- iov_len表示数据的长度
writenv
writev函数用于在一次函数调用中写多个非连续缓冲区<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,有时也将这该函数称为聚集写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"highlight c++"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"code"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"meta"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">#"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">include "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"string"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"><sys/uio.h> "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"line"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"function"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">ssize_t "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"title"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">writev"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"params"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">("bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int filedes, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">const "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"keyword"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">struct iovec *iov, "bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">"type"bd bd-beg">"bd-box"bd bd-beg">">"bd bd-beg">">int iovcnt);
|
filedes表示文件描述符
iov为前述io向量机制结构体iovec
iovcnt为结构体的个数
若成功则返回已写的字节数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若出错则返回-1<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。writev以顺序iov[0]<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,iov[1]至iov[iovcnt-1]从缓冲区中聚集输出数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。writev返回输出的字节总数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通常<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它应等于所有缓冲区长度之和<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
特别注意<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>: 循环调用writev时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,需要重新处理iovec中的指针和长度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该函数不会对这两个成员做任何处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。writev的返回值为已写的字节数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但这个返回值“实用性”并不高<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因为参数传入的是iovec数组<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,计量单位是iovcnt<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,而不是字节数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们仍然需要通过遍历iovec来计算新的基址<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,另外写入数据的“结束点”可能位于一个iovec的中间某个位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,因此需要调整临界iovec的io_base和io_len<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
do_request()
浏览器端发出HTTP请求报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器端接收该报文并调用process_read对其进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,根据解析结果HTTP_CODE<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进入相应的逻辑和模块<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
其中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器子线程完成报文的解析与响应<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;主线程监测读写事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,调用read_once和http_conn::write完成数据的读取与发送<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。

process_read函数的返回值是对请求的文件分析后的结果<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一部分是语法错误导致的BAD_REQUEST<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一部分是do_request的返回结果.该函数将网站根目录和url文件拼接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,然后通过stat判断该文件属性<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。另外<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为了提高访问速度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过mmap进行映射<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将普通文件映射到内存逻辑地址<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为了更好的理解请求资源的访问流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里对各种各页面跳转机制进行简要介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。其中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,浏览器网址栏中的字符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即url<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以将其抽象成ip:port/xxx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,xxx通过html文件的action属性进行设置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
m_url为请求报文中解析出的请求资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以/开头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也就是/xxx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,项目中解析后的m_url有8种情况<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
m_url为请求报文中解析出的请求资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以/开头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也就是/xxx<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,项目中解析后的m_url有8种情况<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
/
GET请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到judge.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即欢迎访问页面
/0
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到register.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即注册页面
/1
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到log.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即登录页面
/2CGISQL.cgi
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进行登录校验
验证成功跳转到welcome.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即资源请求成功页面
验证失败跳转到logError.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即登录失败页面
/3CGISQL.cgi
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,进行注册校验
注册成功跳转到log.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即登录页面
注册失败跳转到registerError.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即注册失败页面
/5
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到picture.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即图片请求页面
/6
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到video.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即视频请求页面
/7
POST请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,跳转到fans.html<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即关注页面
process_write()
根据do_request的返回状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器子线程调用process_write向m_write_buf中写入响应报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
add_status_line函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,添加状态行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:http/1.1 状态码 状态消息
add_headers函数添加消息报头<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内部调用add_content_length和add_linger函数
content-length记录响应报文长度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用于浏览器端判断服务器是否发送完数据
connection记录连接状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,用于告诉浏览器端保持长连接
add_blank_line添加空行
上述涉及的5个函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,均是内部调用add_response函数更新m_write_idx指针和缓冲区m_write_buf中的内容<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
响应报文分为两种<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一种是请求文件的存在<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过io向量机制iovec<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,声明两个iovec<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,第一个指向m_write_buf<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,第二个指向mmap的地址m_file_address<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;一种是请求出错<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这时候只申请一个iovec<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,指向m_write_buf<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
http_conn::write()
服务器子线程调用process_write完成响应报文<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,随后注册epollout事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。服务器主线程检测写事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并调用http_conn::write函数将响应报文发送给浏览器端<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
该函数具体逻辑如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
在生成响应报文时初始化byte_to_send<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,包括头部信息和文件数据大小<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。通过writev函数循环发送响应报文数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,根据返回值更新byte_have_send和iovec结构体的指针和长度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并判断响应报文整体是否发送成功<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
若writev单次发送成功<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,更新byte_to_send和byte_have_send的大小<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若响应报文整体发送成功,则取消mmap映射,并判断是否是长连接.
若writev单次发送不成功<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,判断是否是写缓冲区满了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
若不是因为缓冲区满了而失败<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,取消mmap映射<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,关闭连接
若eagain则满了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,更新iovec结构体的指针和长度<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并注册写事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,等待下一次写事件触发<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(当写缓冲区从不可写变为可写<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,触发epollout<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,因此在此期间无法立即接收到同一用户的下一请求<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,但可以保证连接的完整性<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
05定时器处理非活动连接
由于非活跃连接占用了连接资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,严重影响服务器的性能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,通过实现一个服务器定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,处理这种非活跃连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,释放连接资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。利用alarm函数周期性地触发SIGALRM信号,该信号的信号处理函数利用管道通知主循环执行定时器链表上的定时任务.
非活跃<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是指客户端<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(这里是浏览器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)与服务器端建立连接后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,长时间不交换数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一直占用服务器端的文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,导致连接资源的浪费<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
定时事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是指固定一段时间之后触发某段代码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由该段代码处理一个事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如从内核事件表删除事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并关闭文件描述符<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,释放连接资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是指利用结构体或其他形式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将多种定时事件进行封装起来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里只涉及一种定时事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即定期检测非活跃连接<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里将该定时事件与连接资源封装为一个结构体定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
定时器容器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是指使用某种容器类数据结构<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将上述多个定时器组合起来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,便于对定时事件统一管理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,项目中使用升序链表将所有定时器串联组织起来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
本项目中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器主循环为每一个连接创建一个定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并对每个连接进行定时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。另外<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,利用升序时间链表容器将所有定时器串联起来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若主循环接收到定时通知<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则在链表中依次执行定时任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
Linux下提供了三种定时的方法:
三种方法没有一劳永逸的应用场景<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,也没有绝对的优劣<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。由于项目中使用的是SIGALRM信号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里仅对其进行介绍<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,另外两种方法可以查阅游双的Linux高性能服务器编程 第11章 定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,利用alarm函数周期性地触发SIGALRM信号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数利用管道通知主循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主循环接收到该信号后对升序链表上所有定时器进行处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,若该段时间内没有交换数据<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,则将该连接关闭<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,释放所占用的资源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
从上面的简要描述中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以看出定时器处理非活动连接模块<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主要分为两部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其一为定时方法与信号通知流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其二为定时器及其容器设计与定时任务的处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
基础API<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述sigaction结构体<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、sigaction函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、sigfillset函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、SIGALRM信号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、SIGTERM信号<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、alarm函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、socketpair函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、send函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
信号通知流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,介绍统一事件源和信号处理机制<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
代码实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对信号处理函数的设计与使用进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
信号通知流程
Linux下的信号采用的异步处理机制<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数和当前进程是两条不同的执行路线<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当进程收到信号时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,操作系统会中断进程当前的正常流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,转而进入信号处理函数执行操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,完成后再返回中断的地方继续执行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
为避免信号竞态现象发生<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理期间系统不会再次触发它<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。所以<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为确保该信号不被屏蔽太久<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数需要尽可能快地执行完毕<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
一般的信号处理函数需要处理该信号对应的逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当该逻辑比较复杂时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数执行时间过长<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,会导致信号屏蔽太久<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
这里的解决方案是<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数仅仅发送信号通知程序主循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将信号对应的处理逻辑放在程序主循环中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由主循环执行信号对应的逻辑代码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
统一事件源
统一事件源<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,是指将信号事件与其他事件一样被处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数使用管道将信号传递给主循环<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号处理函数往管道的写端写入信号值<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主循环则从管道的读端读出信号值<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用I/O复用系统调用来监听管道读端的可读事件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这样信号事件与其他文件描述符都可以通过epoll来监测<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从而实现统一处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
信号处理机制
每个进程之中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,都有存着一个表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,里面存着每种信号所代表的含义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核通过设置表项中每一个位来标识对应的信号类型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。

信号的接收
接收信号的任务是由内核代理的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当内核接收到信号后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,会将其放到对应进程的信号队列中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时向进程发送一个中断<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使其陷入内核态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。注意<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,此时信号还只是在队列中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对进程来说暂时是不知道有信号到来的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
信号的检测
进程从内核态返回到用户态前进行信号检测
进程在内核态中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从睡眠状态被唤醒的时候进行信号检测
进程陷入内核态后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,有两种场景会对信号进行检测<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:
当发现有新信号时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,便会进入下一步<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,信号的处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
信号的处理
( 内核 )信号处理函数是运行在用户态的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,调用处理函数前<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,内核会将当前内核栈的内容备份拷贝到用户栈上<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并且修改指令寄存器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(eip<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)将其指向信号处理函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
( 用户 )接下来进程返回到用户态中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,执行相应的信号处理函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
( 内核 )信号处理函数执行完成后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,还需要返回内核态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,检查是否还有其它信号未处理<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
( 用户 )如果所有信号都处理完成<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,就会将内核栈恢复<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(从用户栈的备份拷贝回来<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)<h-char class=“bd bd-beg“>,同时恢复指令寄存器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“>(eip<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>)将其指向中断前的运行位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最后回到用户态继续执行进程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
至此<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个完整的信号处理流程便结束了<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,如果同时有多个信号到达<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,上面的处理流程会在第2步和第3步骤间重复进行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
定时器容器
项目中的定时器容器为带头尾结点的升序双向链表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体的为每个连接创建一个定时器<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将其添加到链表中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并按照超时时间升序排列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。执行定时任务时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将到期的定时器从链表中删除<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
从实现上看<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,主要涉及双向链表的插入<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,删除操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中添加定时器的事件复杂度是O(n),删除定时器的事件复杂度是O(1)<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
升序双向链表主要逻辑如下<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,具体的<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,
创建头尾节点<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中头尾节点没有意义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,仅仅统一方便调整
add_timer函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将目标定时器添加到链表中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,添加时按照升序添加
若当前链表中只有头尾节点<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直接插入
否则<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将定时器按升序插入
adjust_timer函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当定时任务发生变化,调整对应定时器在链表中的位置
客户端在设定时间内有数据收发,则当前时刻对该定时器重新设定时间<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,这里只是往后延长超时时间
被调整的目标定时器在尾部<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,或定时器新的超时值仍然小于下一个定时器的超时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不用调整
否则先将定时器从链表取出<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,重新插入链表
del_timer函数将超时的定时器从链表中删除
常规双向链表删除结点
06日志系统
同步/异步日志系统主要涉及了两个模块<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个是日志模块<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个是阻塞队列模块,其中加入阻塞队列模块主要是解决异步写入日志做准备.
- 自定义阻塞队列
- 单例模式创建日志
- 同步日志
- 异步日志
- 实现按天<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、超行分类

日志<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由服务器自动创建<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并记录运行状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,错误信息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,访问数据的文件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
同步日志<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,日志写入函数与工作线程串行执行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,由于涉及到I/O操作<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当单条日志比较大的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同步模式会阻塞整个处理流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,服务器所能处理的并发能力将有所下降<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,尤其是在峰值的时候<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,写日志可能成为系统的瓶颈<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
生产者-消费者模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并发编程中的经典模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。以多线程为例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,为了实现线程间数据同步<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,生产者线程与消费者线程共享一个缓冲区<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其中生产者线程往缓冲区中push消息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,消费者线程从缓冲区中pop消息<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
阻塞队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将生产者-消费者模型进行封装<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用循环数组实现队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,作为两者共享的缓冲区<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
异步日志<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将所写的日志内容先存入阻塞队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,写线程从阻塞队列中取出内容<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,写入日志<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
单例模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,最简单也是被问到最多的设计模式之一<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,保证一个类只创建一个实例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,同时提供全局访问的方法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
本项目中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,使用单例模式创建日志系统<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,对服务器运行状态<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>、错误信息和访问数据进行记录<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该系统可以实现按天分类<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,超行分类功能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,可以根据实际情况分别使用同步和异步写入两种方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
其中异步写入方式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,将生产者-消费者模型封装为阻塞队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,创建一个写线程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,工作线程将要写的内容push进队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,写线程从队列中取出内容<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,写入日志文件<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
日志系统大致可以分成两部分<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其一是单例模式与阻塞队列的定义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,其二是日志类的定义与使用<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
单例模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述懒汉与饿汉两种单例模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并结合线程安全进行讨论<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
生产者-消费者模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述条件变量<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,基于该同步机制实现简单的生产者-消费者模型<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
代码实现<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对阻塞队列的设计进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
单例模式
单例模式作为最常用的设计模式之一<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,保证一个类仅有一个实例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并提供一个访问它的全局访问点<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,该实例被所有程序模块共享<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
实现思路<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>:私有化它的构造函数<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,以防止外界创建单例类的对象<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;使用类的私有静态指针变量指向类的唯一实例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并用一个公有的静态方法获取该实例<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
单例模式有两种实现方法<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,分别是懒汉和饿汉模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。顾名思义<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,懒汉模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即非常懒<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,不用的时候不去初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,所以在第一次被使用时才进行初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;饿汉模式<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,即迫不及待<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,在程序运行时立即初始化<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
阻塞队列
阻塞队列是一种特殊的队列数据结构<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,它具有阻塞功能<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。当队列为空时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,从队列中取元素的操作会被阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到队列中有新的元素被加入<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;同样地<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,当队列已满时<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,向队列中添加元素的操作也会被阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到队列中有空闲位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
通过阻塞队列<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,我们可以很方便地实现线程间的协作和同步<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。比如<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,一个线程可以往阻塞队列中添加任务<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,另一个线程可以从队列中取出任务并执行<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。如果队列为空<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,取任务的线程就会被阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到有新的任务被添加到队列中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>;如果队列已满<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,添加任务的线程就会被阻塞<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,直到队列中有空闲位置<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
07WebServer
总流程

流程图<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,描述服务器从报文中提取出用户名密码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,并完成注册和登录校验后<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,实现页面跳转的逻辑<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
载入数据库表<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码将数据库中的数据载入到服务器中<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
提取用户名和密码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对报文进行解析<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,提取用户名和密码<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
注册登录流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对描述服务器进行注册和登录校验的流程<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。
页面跳转<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>,结合代码对页面跳转机制进行详解<span class=<h-char class=“bd bd-beg“>“bd-box<h-char class=“bd bd-beg“>“><h-char class=“bd bd-beg“>。