1. http协议
在calserver
中,我们自己手写了应用层协议,并完成序列化与反序列化的操作,但我们的协议不够完善,没法应对各种数据,且如果程序员每次写服务器都要自己实现,过于麻烦;实际上,已经有十分完善且成熟的应用层协议供我们使用----http
协议
http(HyperText Transfer Protocol
),超文本传输协议,它规定了客户端(通常是浏览器)与服务器之间通信时超文本的传输方式
客户端发送http请求,服务器收到后发回http响应,这是双方使用http协议通信的基本流程
http协议是无状态、无连接的,每次发送请求都要重新建立连接,服务器也不会保存客户端的状态信息
1.1 URL
我们平时使用网址访问某个网站时,这个网址就是URL
,它主要有三部分,协议、域名、资源路径
http/https
代表我们以什么协议访问
访问任何服务器上的一个服务,都必须通过IP地址+端口号的方式,而域名就相当于服务器的IP地址,它在浏览器的解释下会被转换成IP地址(DNS技术);而由于http
是知名协议,它与80号端口固定捆绑,大多数使用http
协议的服务器都是绑定80号端口,当浏览器访问服务器时,会自动在域名后加上80号端口,这样也能提高用户的体验
后台服务器大部分使用的都是linux
系统,而在linux
系统中,一切皆文件,任何视频、音频、图片都是文件,定位一个文件的方式就是路径;因此,我们在URL
通过资源路径就能找到相应的资源
这里资源路径中的根目录,在服务器中实际上是Web
根目录,在后面的代码中会体现
1.2 编码
当我们在浏览器中搜索时,填入一些特殊字符,浏览器会自动将这些字符进行编码,以特殊的形式传递,服务器收到后在解码
2. http请求与响应
我们先来简单看看http请求与响应的样子,再来具体分析内部的细节
将TcpServer
的代码稍作修改,当服务器获取连接时,一并将获取数据也处理了,将处理后的数据交给HttpServer
;HttpServer
中,对数据处理返回处理结果,再由TcpServer
发回去
可以看到,http的请求与响应是有一定的格式的
2.1 http请求与响应格式
其中换行符规定为\r\n
,请求正文可以为空
2.2 http服务器
http协议是基于tcp
协议完成数据的传输的,由于tcp
面向字节流传输的特性,当http服务器收到reqstr
时,它一定是被浏览器序列化过,是一个字符串形式的数据的;http服务器就需要对它进行反序列化,拿到客户端要请求的资源路径以及各项属性
因此,http服务器需要知道两件事:
- 如何将报头与有效载荷分离?
- 如何分用?
对于问题2,用户直接拿到了请求,已经解决
通过空行,就能将报头与有效载荷分离,那么如何得知响应正文的长度呢?因此,在报头中,会有一条属性Content-Lenth: xxx
,来标识正文的长度
接下来我们正式编写http服务器
2.2.1 基本通信
首先是拿到每一行的内容
经过测试拿到每一行的过程无误
再将每一行中的内容划分,拿到每一条属性
接下来我们就可以根据uri
中的路径在服务器中找到对应的资源;一般情况下,在linux
服务器中,我们将所有的资源都放在同一目录wwwroot
下,当用户请求的是根目录/xxx
下的资源,我们将uri
进行处理,拼接成wwwroot/xxx
,这样所有资源都去wwwroot
下查找,我们把wwwroot
就称为Web根目录
我们平时访问一个网站时,他会默认给我们展示网站的首页,这里也类似处理
接下来就可以根据uri
中的路径,读取文件内容,构建响应,将响应序列化,发送回客户端
这里首页就随便写,用来测试;启动服务器之后,浏览器连接
2.2.2 网页中的请求
当我们在主页中添加几张图片,再来看看请求
我们发现浏览器会自动帮我们请求网页中的图片资源
因此,当网页中需要服务器上的其他资源,浏览器会自动帮我们请求
2.2.3 页面跳转
我们经常在同一网站上的不同页面之间跳转,这是如何做到的?
网站不仅有首页,还有登录、注册、内容页面等,在html
中使用超链接标签,当点击超链接时,浏览器就会自动向服务器发送请求
2.3. http常见Header
Content-Length
:正文的长度Content-Type
:数据类型Host
:客户端告诉服务器,所请求的资源在哪台主机哪个端口上的,在代理服务器中有用User-Agent
:客户端的主机信息Referer
:从哪个页面跳转过来的Connection
:管理长连接Location
:配合3xx状态码使用,告诉客户端应该去哪里访问Cookie
:会话管理
2.3.1 Content-Type
当http服务器响应时,应该告诉浏览器数据类型,以便浏览器解析,上面我们的响应中图片的Content-Type
是html
,但浏览器照样能自动识别数据类型,这是因为目前浏览器功能十分强大,它能自主判断,但我们最好还是加上
2.3.2 Connection
Connection
是用来管理长连接的一个报头字段,由于http是无连接的,每次发送请求都需要与服务器重新建立连接;如果将该字段设置为keep-alive
,允许发送完http请求或响应后不立即关闭tcp
连接
2.4 http状态码
1xx
:Informational(信息性状态码,请求正在被处理)2xx
:Success(成功状态码,请求处理完毕)3xx
:Redirection(重定向状态码,配合Location
报头字段,客户端接下来要去的地方)4xx
:Client Error(客户端错误状态码)5xx
:Server Error(服务器错误状态码)
2.4.1 重定向状态码
重定向状态码常用的有两个:
- 301:永久重定向(状态码描述:Moved Permanently)
- 302:临时重定向(状态码描述:Found)
有时,服务器可能需要更改域名,但客户不知道,为了让客户更好的找到新服务器地址,当客户访问旧服务器时,旧服务器返回重定向状态码,并加上Location
报头字段,标明新服务器的地址,浏览器收到响应后,发现是重定向,再次向Location
中的地址发送请求
永久重定向表示服务器地址永久更换,当浏览器收到永久重定向的响应后,如果本地保存了旧服务器的地址,则会自动更改为新服务器的地址
临时重定向表示服务器地址暂时更改,下一次浏览器请求的还是旧服务器
2.4.2 客户端错误状态码
大家最常见的客户端错误状态码:
- 404(状态码描述:Not Found)
当你请求的资源在服务器中不存在时,就会返回404状态码,并响应一个404html页面
2.5 http的方法
常用的http请求方法有两个:
- GET:一般用于向服务器请求资源,也可以通过
uri
向服务器传递参数 - POST:可以通过请求正文向服务器传递参数
用Postman
软件来模拟GET和POST的传参
GET和POST传参有何用处呢?这需要结合html中的form
表单来使用
当结合form
表单时,输入用户名密码,点击提交按钮,浏览器就会以action
中的路径+参数向服务器发送请求
我们在http服务器之外写好业务逻辑,将各种业务(登录、注册、搜索等)传入http服务器中,当服务器对参数提取后,将请求交给业务逻辑,这样业务中就拿到的用户的数据,处理完毕后,返回一个响应
这样就完成了我们常见的登录、注册等功能
无论是GET还是POST传参,在传输过程中数据都是不安全的,因为很明显,数据是可见的;因此,数据的传输需要进行加密,这就涉及到https
协议
3.Cookie和Session
在访问某些需要登录的网站时,我们会发现这些现象:
- 登录一次过后,一段时间内都不再需要登录了,网站默认就认识是哪个用户
- 网站能识别我是不是VIP或其他用户,从而让不让我观看某些资源
根据我们对http协议的认识,它是无状态、无连接的,服务器不会保存客户端的状态信息,那么服务器是怎么识别我的身份呢?
虽然http是无状态、无连接的,但是为了提高用户的体验,http协议引入Cookie技术,来进行会话管理
3.1 Cookie的工作原理
当第一次登录输入用户名密码,向服务器发送请求,服务器会在响应报头中添加Set-Cookie
字段,值是传递给服务器的参数,浏览器收到响应后解析,并在下次访问该网站时,加上Cookie
报头字段,自动提交了用户名和密码
3.2 Cookie的类别
Cookie可以分为两种:
- 内存级:浏览器关闭Cookie信息就销毁
- 文件级:Cookie有明确的过期时间
3.3 Cookie的使用
Cookie的安全问题:由于cookie信息可能是保存在文件中的,如果该文件到盗取,用户的私人信息也就泄漏了,为了解决安全问题,Cookie通常与Session一起使用
Session是用来跟踪用户与服务器交互期间用户状态的一种机制
3.4 Session的工作原理
3.5 Session的使用
当用户第一次登录时,创建一个Session
对象,并用map统一管理
用户下次登录时,服务器将请求中带有session_id
的Cookie字段提取出来,并查找;如果该id已经存在,拿出对应的Session
对象,这样服务器就知道了客户端的信息
至于Session
中,可以存储该用户是VIP还是普通用户,或者其他属性,进而给不同的用户推送不同的内容
在实际的应用中,经常使用Cookie + Session的方式来进行会话管理的工作
但需要注意的时,由于session_id也是明文传输的,仍可能被盗取,用户的身份仍然可能被冒认
仅仅使用Cookie,我们面临的问题:
- 用户私密信息泄漏
- 用户身份被冒认
Cookie + Session的方式,解决了第一个问题,第二个问题虽无法根除,但由于用户的信息是由服务器统一管理的,服务器可以通过一定的算法检测当前用户是否是合法用户,一旦发现用户有问题,直接将session_id失效即可
4. https协议
4.1 https是什么?
https是在http的基础上,对要传输的数据进行了加密,我们把http+加密算法称为https协议
至于为什么要加密,在前面的代码中也有所体现,浏览器给服务器发送的请求是明文的,一方面他能被任何人获取;另一方面,我们的数据是通过中间人(运营商),再发送给服务器的,如果不加密,中间人有可能会劫持我们的数据,进而对数据进行修改等操作
4.2 什么是加密?
将明文通过密钥转换成密文的过程就称为加密
4.3 加密的方式
- 对称加密
- 加密和解密使用同一把密钥
- 特点:加密速度块
- 只使用非对称加密
- 公钥加密,私钥解密(私钥加密,公钥解密),私钥只有自己知道
- 特点:加密速度慢
- 双方都是使用非对称加密
- 对称加密 + 非对称加密
4.4 数据摘要/数据指纹
将数据进行哈希算法,形成一串几乎具有唯一标识字符串,将该字符串就成为数据摘要或者数据指纹
如果原来的数据被修改,哪怕是一点点,经过同样的哈希算法所形成的字符串一定不一样,可以借此判断原数据是否被篡改
数据摘要的应用:
-
云盘的秒传功能
-
用户数据的存储
4. 5 加密方案
4.5.1 对称加密
4.5.2 只使用非对称加密
4.5.3 双方都用非对称加密
4.5.4 非对称加密 + 对称加密
4.5.5 中间人攻击
中间人有可能在双方通信之间就已经到来,当server把公钥S发送给client时,中间人拿到S后,将自己的公钥M发送给client;client因为M就是server的公钥,将自己的密钥C加密,发送server;中间人拿到密文后,用自己的私钥M’解密,就拿到了密钥C;再把C用server的公钥S加密,发送给server,server照常解密,确实拿到了client的公钥C
但中间人也拿到了之后双方通信的密钥C,数据也就不在安全
该过程中最关键的问题在于client无法识别自己收到的公钥是否是合法的;因此,如何让client识别收到的公钥是合法的就是关键
4.6 数据签名
对数据的签名形成数据签名实际是一种算法;在这个过程中,只有签名者能签名,因为只有签名者有自己的私钥,有对数据摘要加密的能力
这里的签名者就是CA机构,而签名于数据合起来的数据签名就是证书
4.7 证书
从签名的过程来看,证书实际就是一段数据 + 签名
当client第一次请求server时,server实际返回的是一个证书,证书中包含了签名,server端的公钥、域名等信息
server的证书又是从哪来的呢?证书是由CA机构或者其授权的子机构颁发的
以下是文心一言对CA机构的简述:
一款服务器要能被大家使用,写好服务器只是第一步,接下来服务器的负责人需要去当地的CA机构申请证书,只有拿到证书,服务器才能被使用
向CA机构申请证书的过程中,需要提交服务器域名、服务器公钥、申请者等数据信息,CA机构对公司进行审核过后,确认公司合法,使用CA机构的私钥对数据进行签名形成证书,颁发给申请人
而所有的浏览器中,都要内置所有可信任CA机构或其授权子机构的公钥
如果中间人修改证书,将服务器公钥为自身的公钥M,那么client一定认证失败,也就知道了收到的公钥是非法的
为什么数据摘要一定要加密形成签名?直接将数据摘要和数据结合形成证书发给client不行吗?
如果是这样,client收到证书后将数据和数据摘要分开,对数据使用哈希算法,判断两个数据摘要是否相等来验证公钥是否合法,看似可行,但如果中间人对修改后的数据使用哈希算法,在把新形成的摘要与修改的数据形成证书发给client,client也无法识别
因此,数据摘要必须密文传输,只有持有私钥的人才能加密
为什么不直接加密数据获得签名,再将签名与数据结合形成证书?
通过哈希算法减小数据长度 ,提高加密效率