目录
一.功能
二.技术特点
三.主要框架
四.CGI技术
五.流程
六.具体实现细节
6.1 套接字部分
6.2 线程池部分
6.3 协议处理部分(主要部分)
七.测试结果
一.功能 实现一个自主开发的小型web服务器。可以解决用户的基本网页请求。主要是静态网页的返回和动态网页的返回。
二.技术特点
- 网络编程(http协议,TCP/IP协议,socket流式套接字)
- 多线程技术(线程池)
- CGI技术
四.CGI技术 CGI技术全称Common Gateway Interface(通用网关接口)。
作用是能够让服务器方便的调用外部程序(CGI程序)。CGI本身是一套协议和规范,体现在数据传输上。原则上只要拥有读写文件功能的编程语言都可以用来编写CGI程序。注意区分CGI技术和CGI程序,是两个不同的概念。CGI技术是一个标准,CGI程序,是处理数据的程序。
服务器确认需要使用调用CGI程序来处理数据时,为了Web服务器的安全,需要创建子进程,运用进程替换,替换到CGI程序执行。由于数据在Web服务器进程中,需要将参数传递给子进程。并且子进程需要将处理结果发送给web服务器进程。
由于程序替换不会替换文件描述符和环境变量,环境变量具有全局属性。
在父进程传递参数时,可以通过设置环境变量来进程参数的传递或者通过管道来进程参数的传递。如果通过管道进行参数的传递,替换进程对于管道的文件描述符不明确,我们可以在未替换进程前,将管道重定向到标准输入,替换进程从标准输入里读管道数据即可。
在替换进程传递结果时,只能通过管道将结果发送给父进程。需要创建进程前,创建管道,由于替换进程对于管道文件描述符不明确,需要在为替换进程前,将管道重定向到标准输出,替换进程只需要将处理完的数据写道标准输出即可。父进程从管道读数据即可。
总结:在HTTP协议中,GET或者POST请求方法有参数,需要使用CGI技术出来参数后返回。
web服务器进程向CGI程序发送数据有两种方式环境变量和管道。如果使用管道,需要将管道重定向到标准输入。
CGI程序向父进程发送数据只能通过管道,需要将管道重定向到标准输出。将数据写道标准输出中,父进程从管道拿数据。
注意:如果web服务器使用管道传递数据给CGI进程,由于管道时单向的,需要创建两个进程。
五.流程 web服务器从网络套接字中收到用户请求。解析请求,分析请求是GET请求方法还是POST请求方法。
如果是GET请求方法,如果带参,运用CGI技术处理完参数,再构建响应返回。不带参直接构建响应返回。
如果是POST请求方法,如果协议中有请求正文,运用CGI技术处理,再构建响应返回。
如果访问的是资源是一个可执行程序,也需要运用CGI技术处理。
文章图片
六.具体实现细节 项目主要包括
- HttpServer.hpp:web服务器的起点
- ThreadPool.hpp:多线程部分
- Sock.hpp:关于网络套接字部分
- Protocol.hpp:关于协议处理部分,获得请求,分析请求,制作响应,发送响应。
- Util.hpp:与协议无关的处理,比如:字符串转整形
- Log.hpp:日志
建立套接字,设置端口复用(setsockopt),绑定套接字(端口号和IP地址),监听套接字(listen),等待连接请求的到来。
6.2 线程池部分 一次性创建多个线程,避免来一个请求再创建一个线程的开销。使用线程池中的线程来处理请求,构建响应返回,并且将线程池设成单例模式(懒汉模式),只有一个线程池对象。
注意:线程处理的任务时一个个请求。
6.3 协议处理部分(主要部分) http协议格式
文章图片
①获得请求并细分:
根据HTTP协议请求包括四个部分请求行,请求报头,空行,请求正文。其中请求行包括请求方法,url,协议版本。请求报头是一些属性信息,以KV形式呈现。报头和正文以空行分隔。
收到请求,需要将其拆分成4个部分,请求行,请求报头,空行,请求正文。每个部分还需要细分。由于协议是以行为单位,所以按行读取,由于不同客户端制作的协议行分隔符可能不同(\r,\n,\r\n)。所以按字符读取,我们获取后 统一以\n结尾。
请求行:
读取一行,将请求行细分为请求方法,url,协议版本。以空格分隔。
请求报头:
循环按行读取,读取到空行结束。保存在map中,方便查找。
空行:
在读取报头时可以一起读上来。
请求正文:
GET方法没有正文,不需要读取正文。
POST方法,如果在报头中有一个属性Content_length表示正文字节数。如果大于0,需要读取正文,按字符读取,读取content_length个字符。
②分析请求
介绍url:
文章图片
主要处理GET和POST方法,其它方法直接构建响应返回,状态码为404。
分析请求主要是获得请求资源的路径和参数。请求资源的路径在url中。注意:在web的目录中都会有一个text.html文件。
请求的资源从web根目录开始。GET和POST方式传参方式不同,GET方法参数在url中,POST在正文中
如果是GET方法:
参数和路径的分隔符是"?",这个"?"是唯一的,如果查询"?"会被urlencode转化。如果不存在"?"说明没有参数。存在"?"在url中找到"?"前面为路径,后面为参数。
如果是POST方法:
路径直接是url,有请求正文的话,参数是请求正文。
其中GET有参数,POST有参数需要使用CGI技术来处理参数。
判断请求资源:
请求资源不存在,直接构建响应返回,状态码为404。
如果是一个目录,说明访问下的该目录下的text.html文件。
如果是可执行程序,需要使用cgi技术处理。
如果是一个普通网页文件,说明直接就是访问该文件内容。
③构建响应返回
以行为单位。根据HTTP协议响应包括四个部分状态行,响应报头,空行,响应正文。其中状态行包括协议版本,状态码,状态码描述。响应报头是一些属性信息,以KV形式呈现。报头和正文以空行分隔。
- 构建响应行:
- 构建响应报头:
- 构建空行:
- 构建响应正文:发送响应正文。构建响应正文需要区分是否使用CGI技术。
GET无参,POST无参,普通网页文件,直接打开文件,将文件内容发送过去。
2.使用CGI技术:
创建子进程,使用进程替换,替换到CGI程序。替换进程主要替换子进程的数据和代码。通过环境变量和进程间通信技术来传数据。
参数传递:参数POST方式参数用管道发送给替换进程,替换进程不知道管道文件描述符,需要将管道重定向到标准输入。替换进程从标准输入那参数。GET方式,通过环境变量发送给替换进程。请求方式通过环境变量发送给替换进程(通过请求方式来确定从哪里获取参数),正文大小通过环境变量发送给替换进程(POST需要读取正文)。
【项目|自主小型Web服务器实现——TinyHttp】结果传递:替换进程不清楚管道文件描述符,重定向管道到标准输出,将处理的结果从标准输出传递给父进程。
web服务器将处理的结果发送到网络中。
最后需要将Web服务器守护进程化。
守护进程是特殊子进程,与终端无关。调用daemon系统调用。
gitee源码:CODE: 保存学习重要代码
七.测试结果
- GET不带参数
文章图片
- GET带参
文章图片
- POST带参
文章图片
文章图片
推荐阅读
- 线程池
- python打包flask|python打包flask 项目_手把手教你将Flask应用封装成Docker服务的实现
- 计算机基础|[计算机网络]——知识点总结
- 面试题|【Web前端面试】葵花宝典(2022版本)——HTTP\浏览器 篇
- JAVA|Springboot+Vue
- 笔记|CSS设置背景颜色透明
- http|Centos 7 安装MySQL报错(The GPG keys listed for the “MySQL 5.7 Community Server“ repository are)
- thingsboard|ThingsBoard 使用 REST API HTTP 方法获取遥测数据 属性数据等
- 爬虫|关于blob加密视频的基于m3u8和ts文件下载转MP4视频的python爬虫实现