文章目录
再谈协议
之前CS交互实现的都是发送字符串,如果我们要发送的是结构化的数据呢?比如发送QQ时除了内容之外,还佩戴着名称时间头像等构成的结构化数据整体传送(才有意义),需要转化为一个长的字符串。
什么是序列化,什么是反序列化?
需要将结构体的对象转化为字符串,传递给网络的过程叫做序列化。
对端主机将网络中的字符串转化为结构化的数据叫做反序列化。
为什么要进行序列化和反序列化?
结构化的数据是不便于网络传输的,需要将结构体的对象转化为字符串
- 字符串便于网络传输。
- 为了方便上层进行使用结构化数据内部成员变量,将应用和网络进行解耦。
应用只需要考虑结构化的成员就行,根本不关心怎么在网络中的传输和形态模式。- 那么我们之前的tcp,udp,没有进行任何的序列化和反序列化。没有应用场景无法定制序列化数据结构。
- 结构化的数据本质就是协议的表现。
怎么进行序列化和反序列化呢?
(json组件)使用别人的。
实验设计应用场景:实现网络版本的计算器
发送数据时将这个结构体按照一个规则转化为字符串,接收数据的时候再按照相同的规则把字符串抓互为结构体。
CalClient和Calserver之间使用协议进行通信。
-
定制协议协议的过程,就是定制结构化数据的过程
请求格式
响应格式
result能否区分正常计算结果还是异常的计算结果。
约定计算完毕的状态。 -
编写服务器
业务逻辑:做一个短连接服务,requwst->分析->构建response->send(response)
读取请求,分析请求/计算结果,构建响应并返回,关闭连接. -
结果:(未添加json的一般版本)
- 那么双方是怎么知道x就是第一个操作数?这就是约定。结构化数据+约定刚才制定了一个协议,server client都得遵守这就是自定义协议。
添加序列化反序列化使得传递的都是字符串之后的单次输入的->
网络计算器版本
命令行安装json:
目的就是将网络中传递的结构体全部转化为字符串,是通过插件,jsoncpp库,头文件jsoncpp/json/json.h。具体详见代码test_json/test.cc
- 反序列化的过程:
客户端:
- 发送的时候:
- 输入接收操作数和操作符存储进结构体
- 序列化为字符串,write入网络中的线程中
- 接收的时候
- 接受的是来自客户端的字符串,采用read进行读操作读进buffer中
- 将读出来的结果反字节序列,转化进结构体中
服务器端
- 接收的时候
- 接收来自客户端发来的字符串,将他的request反字节序列转化为结构体
- 通过对于结构体中的数据进行数据操作,并且数值保存
- 将response结构体先转化为字符串类型,然后用write写入sock线程中,进行返回。
结果:
总结
我们刚刚实现的CS模式的在线版本计算器,本质就是一个应用层网络服务
- 基本的通信代码是自己写的,什么创建连接啥的。=>OSI的会话层
- 序列化和反序列化是我们用组件完成的。=>OSI的表示层
- 请求,结果格式,code含义等约定是我们自定义的。业务逻辑是我们自己完成的。=>应用层
OSI七层协议相比于UDP,TCP的五层,就是将上面三层综合为应用层的一层在TCP UDP中。所以才需要组件才能实现原本应该手写的三层。也算是应用层协议,很多都已经写完了,我们这后面只需要调用就可以了。
http协议本质上,在定位上和我们刚写的计算器没有区别都是应用层协议
- 1.网络通信。2. 序列化反序列化,3. 协议细节在http协议上都已经被大佬实现了,我们只需要用就行。
- 众多协议中我们选择学习研究的是http协议。
指明了协议方案,你用什么网络服务,基本对应的端口都是确定的。mysql就是3306.
http协议
网址URL:定位网络资源的一种方式。统一资源定位:URL
资源:图片,音频文档啥的。
IP+PORT 唯一的确认了一个进程,但是我们无法唯一确认一个资源!
公网IP地址是唯一确认的一台主机,而我们所谓的资源都一定存在于网络中的一台LINUX机器上,OS保存资源的方式都是以文件的方式保存的。单Linux系统标识唯一资源的方式通过绝对路径。
所以,IP+Linux路径就可以唯一的确定一个网络资源!!
ip->通常是以域名的方式呈现的。路径可以通过目录名+ / 来确认。
- URL的组成:协议+域名+路径
- URL会把有些符号特殊处理,对字符进行转码叫做encode。浏览器编码,服务器解码,不想让这些特殊字符影响地址的编写。例如:+:%2B
http
urlencode&urldecode
编码的过程就是由浏览器实现的,对字符进行转码,转为16进制,然后从右到左取4位,每两位做一位,前面加上%,为%XY格式。百度搜索C++,网址如下:
一. 格式认识
-
无论是请求还是响应,基本上http都是按照行为单位进行构建请求和响应的。无论是请求还是响应,几乎都是有3或者4部分组成。
-
http请求内容
上网行为:进程间通信,就是IO的过程
- 从服务器拿数据
- 向服务器发数据
思考:
-
http是如何完成解包,封装,分用的呢?
报文(报头+内容)数据是可以被看做一个长的字符串的,用特殊字符->空行作为分隔符对长字符串进行切割。
而分用不是http解决的,是具体的应用代码解决的,http需要有接口来帮助获取上层参数来做。
-
http的请求或者响应是如何被读取和发送的呢?
http request和http response可以被看成是一个大的字符串的直接发过去,按行的形式进行发送。
-
http(应用层的)底层就是TCP协议
二 代码实现一个http协议下的服务器
从字节流中进行读取,和read差不多。
-
用浏览器访问IP下的端口号的主机,因为后端我们编写对于请求的设置,此时就会将Http请求直接打印出来。
依次是请求行(请求方法get,http_version),请求报头,空行三部分。
报头中会有对端访问主机的OS系统等信息,便于我这里识别并提供响应服务。
-
添加响应信息:
响应的不只是一个字符串或者什么图片,而是应该构建一个完整的响应字符串,包括响应报头,响应行等。
添加Content_type:当响应是文档时:
响应结果:
http协议,如果自己写的话本质就是根据协议内容进行文本分析。
-
安装telnet服务
访问百度,下面是响应信息:
引入Content_length属性
-
http按行读取本身就是一种序列化的形式。在接收缓冲区中如何保证读取完整?
-
用recv读取请求放到buffer中不是正确的用法,只是没有被呈现出来。
http客户端并不是每次都一个个发来http请求,可能一次发来多个。
- 每次读取保证读取一个完整的http request;
- 保证每次读取都不要将下一个http请求的一部分读到后面。
-
那么如何判定我们将报头部分读完了呢?读到空行就说明上面的报头部分完成了。
-
决定读取时后面还有没有正文,和请求方法有关。
-
如果有正文,如何保证把正文中的全部读取完成呢?
在前面已经读取的报头文件当中有着各种属性,其中有一个就是标明正文中有多少个字节的属性->Content-length:len,决定读取多少个Len字节的正文。
也就是说表示了有效载荷有多少,是一个自描述字段。
根据空行作为特殊字符将报头部分读取完整,再根据报头中的Content-Length读取完整的有效载荷,这样可以读取一个完整的报文。
-
这个属性帮助我们读取完整的请求或者响应,同时根据空行实现报头和有效载荷的分离(解包)
-
不存在Content-Length的情况就是不存在正文的时候,除非你违反了http协议。
Content-Type
正文部分的数据类型,对照表中都有。
HTTP方法
http1.0版本一般是短连接:一个请求一个响应之后,close socket.一个请求,一般就是请求一个资源然后链接关闭。
GET方法&&POST方法
http请求的/并不是根目录,而叫做Web根目录。
如果在浏览器请求时指定路径,后端打印请求就会出现目录。
/:我们一般请求的一定要是一个具体的资源。但是如果请求的是/,意味着我们要请求该网站的首页,一般的网站默认都是首页。
在服务器中有对请求做出判断的语句,如果是/就将路径改为首页资源的路径,显示首页作为回应。if(method==‘/’) path=“./wwwroot/index.html”;
- 实验,无论你请求我的什么内容,我都将我的首页返回给你。
其中wwroot就叫做web根目录,wwroot目录下放置的内容,都叫做资源。实验中的html就是网页首页资源。
-
返回时不仅仅是返回网页信息,还需要完整的http请求
-
stat获取指定路径下的文件属性,输出型参数结构体。
-
读取HTML文件采用ifstream打开文件,读取文件添加到响应报文的Content中作为有效载荷。
-
结果:读取到完整网页信息
实验实现简单的http响应是为了帮助验证GET和POST 的方法
w3cschool学习表单
注册或者登录
(前端设置提交方式)
- 提交参数的区别,就是参数在哪里的区别
Get方法如果提交参数,使用url的方式。C++程序就可以提取出用户名和密码。
Post方法是通过正文进行提交参数(输入框中填写的内容)的。
采用抓包软件Fiddler
-
总结
-
GET:方法叫做获取,是最常用的方法,默认一般获取所有的网页,都是GET方法,但是如果需要他提交参数,他是能够完成的。通过URL进行参数,从而提交给server。
POST:方法叫做推送,是提交参数比较常用的方法,但是如果提交参数,一般是通过正文部分提交的,但是你不要忘记,Content-Length,表示参数的长度。
所以获取一般是GET,推送既可以GET也可以POST
-
区别:参数提交的位置不同
- GET方法,不私密,会将私密信息回显到url输入框中,增加被盗取的风险。
POST方法比较私密(私密!=安全),最起码仍然可以用fladder这样的抓包软件拿到信息。不会回显到url输入框。
所以,网络中的数据只要不经过加密,都可以被别人获取就是不安全的。
-
get是通过url传参的,而url是有大小限制的。和具体的浏览器有关。
POST方法是由正文部分传参的,一般大小没有限制.
-
如果提交的参数不敏感数量还小,可以采用GET,否则就使用POST方法。
-
http协议处理本质就是文本分析
http协议本身的字段。提取参数如果有的话,就可以从字符串当中用语言获取。GET &POST其实是前后端交互的一个重要方式
-
2. HTTP响应
Http常见状态码:
应用层是人要参与额,人的水平是不同,http状态码,很多的人不知道如何清除使用,又因为浏览器的种类太多了,导致对状态码的支持并不是太好。类似于404状态码对浏览器没有任何的指导意义,浏览器不做任何处理,只是自己对于他进行代码处理。
- 404输入客户端问题,还是服务器问题?客户端状态码,京东上去找抖音视频,人的问题。
-
500 503 504 基本都是server端出现问题。
-
3XX的状态码是有特殊含义的
永久重定向:301
临时重定向:302,307
-
重定向:
当访问某一个网站的时候,会让我们跳转到另一个网址,
当我访问某种资源的时候,提示登录,跳转到登录页面,登录完成之后,会自动跳转回来。比如王者QQ登录账号。
-
永久:网址重定向之后,第一次提示用户跳转新网址,浏览器会自动访问新的网址,重新发起请求同时更改你浏览器的书签,用新的域名更换掉之前的,网站搬迁,域名更换。
-
临时:当我访问某种资源的时候,提示登录,跳转到登录页面,登录的时候,会自动跳转回来。
-
-
模拟测试:
重定向时需要浏览器(火狐不支持)给我们提供支持的,浏览器需要识别301 302 307
-
301永久性转移
访问前:
-
访问后:
手机访问也ok:
server要告诉浏览器我应该再去哪里,所以需要报文属性,Location:新的地址.
读取成功,构建响应,永久重定向,告诉客户端要去哪里(Location),没有报文,然后直接发送。实现访问之后,响应重定向到新的网址。因为浏览器收到了我们的请求,301重定向新的网址,而客户端并不知道情况。
- 302临时性转移
HTTP常见的Header
Content-Type。Content-Length。Host:你要访问的那台主机
User-Agent:声明用户的OS和浏览器版本信息。referer:当前页面是从哪个页面跳转过来的。
Location:搭配3xx的状态码使用,告诉刻画段接下来的去哪里。
session 和cookie
Connect:keep-alive我们之前的所有的实验,全部都是请求,响应,断开连接,服务器上有的很多资源。一个大型的网页内部,是有非常多个资源组成的,每个资源都需要重新发起http请求,基于短连接的策略。http/1.0:短连接,close,一次请求响应一个资源,需要多次进行http请求,http是基于tcp的,每一次都需要建立连接传输数据断开连接。如果网页资源中有100张图片就需要先进行一次请求获取网页资源,探后再建立10次请求获取完整图片信息构建成网页。
http/1.1之后,支持长连接,keep-alive:这条连接一直不关闭。减少频繁建立新连接的次数,提高效率。
cookie
首先,一个网站的首页是由很多元素构成的,http本身是一种无状态的协议,(无状态:并不记录之前每次的行为和上下文数据,只在乎当前次请求是否成功)每次请求并不知道是谁发起的请求。
但是实际生活中,进入网站时发起请求各种获取资源时跳转网页显示网站是认识我的,在我进行开始一次的登陆之后。
这是矛盾的。如何实现的呢?这并不是http协议本身要解决的问题,http可以提供一些技术支持来保证网站具有“会话保持”得功能,而会话管理就是由cookie和session实现的。
-
浏览器:cookie其实是一个文件,在浏览器中,保存的是我们用户的私密信息。
-
http协议:一旦该网站对应有cookie,在发起任何请求的时候,都会自动携带cookie信息。
-
后续的请求中,每一个请求都会在请求报头属性中,会自动携带对应的cookie,把个人信息等都携带,每访问一个网页都被浏览器自动交给服务器进行认证,所以在登录之后的请求中自动认识你。
-
Set-Cookie:在响应报文中添加这个字段,服务器向浏览器设置一个cookie,访问之后,服务器给浏览器一个返回信息写到浏览器的cookie中,相当于认证工卡,告诉他下次来得时候把认证信息也带来,下次访问就更方便了。如果你不想再让他认识你了,将cookie 文件进行移除就行了,就不认识你了。
-
验证Cookie:添加Set-Cookie属性,完成写入.可以多设置几个cookie将信息分离就行了。
第一次访问之后以后的每次请求都存在Cookie信息了。
-
cookie有两种存在形式(存在形式跟浏览器是很大关系的)
文件版:就在浏览器的安装目录下,关闭了也认识你。
内存版:cookie信息,浏览器这个进程一关闭再启动就会忘记你。
-
也有可能保存浏览痕迹等其他私密信息,访问恶意网址之后,服务器会给cookie 注入木马获取到你的cookie信息,然后再在他的电脑上访问相同的网址,就可以以你的身份访问认证资源。如果当中保存着用户名和密码,就非常危险了。所以单纯的使用cookie是具有一定危险的。
session
将用户的私密信息保存在服务器端,Session可以在多个服务器之间共享 。
保存给浏览器的cookie文件中的是当前用户的session_id(会话id),真正的信息放在了server对应的id的磁盘文件当中,所以id是一个具有唯一性的值(时间戳形成唯一文件命名)。后续的所有的请求都会由浏览器自动携带cookie文件当中的内容(会话id)在服务器中寻找,然后服务器进行身份检查并认证,也是一种会话保持的功能。认证时只需要在服务器中寻找是否存在对应session_id的文件,而不再需要用户名和密码的认证。
但是我们还有cookie文件被盗取的风险!腾讯也保护不了你的QQ号信息。
服务器可以随时让这个sessionID失效,让你重新进行认证,给你创建新的session文件和ID。手机短信验证就很有效。
为什么网站要让用户进行认证?就是因为每次的http请求是无状态的,需要记录访问网站时的上下文数据cookie和session本质是提高用户访问网站或者平台的体验。
Cookie和Session的区别
关联: 在网站的登录过程中,需要进行配合使用
区别:
- Cookie是客户端的存储机制,Session是服务器端的存储机制
- Cookie可以存储各种键值对,还可以存储别的东西.Session是专门用来保存用户的身份信息.
- Cookie是完全可以单独使用的,不搭配session实现非登录场景下.
- Session也可以单独使用.在手机APP登录服务器,服务器也需要Session,此时就没有Cookie的概念,Cookie是和浏览器强相关的.
- Cookie是HTTP协议中的一个部分,Session可以和HTTP协议无关(TCP,webSocket也可以使用Session)
https
粘贴下来的网址前面都是https,相当于做了加密。网络中总是被加密的。
- https=http + TLS/SSL (相当于http数据的加密解密层)软件层到系统调用时加了一层。
加密方式
-
对称加密方式,秘钥X(只有一个)
用X加密,也用X解密
比如运算一次,就会进行加密,一个数连续两次一个数,最后得到原值。
经过一次^,得到的数据相当于加密之后的数据,内个数就相当于X,就相当于对称秘钥。
-
非对称加密,有一对秘钥:公钥和私钥
可以用公钥加密,只能用私钥解密,比如RSA。
一般而言,公钥是全世界公开的,私钥是自己进行私有保存的。
-
如何防止文本中的内容被篡改?以及识别到是否被篡改?
通过hash散列方法,形成固定长度的唯一的字符序列,对文本进行改变,哪怕是很小的差异也会造成差异非常大的hash结果,这个字符序列叫做数据摘要or数据指纹。
再经过加密算法(一般是非对称加密),得到加密结果,叫做数据签名。
通信端发送的文本和对应的数据签名作为一个整体,接收端要校验这一串数据签名是否被修改过,将文本和数据签名进行分离,再用相同的hash散列对分离出来的文本生成新的数据摘要;同时,把数据签名通过解密算法得到对应的数据摘要,对比两份数据摘要一样的话,说明没有被篡改。
在HTTPS协议进行数据交互时,双方如何确定是哪个加密算法呢?如何选择加密呢?
- 如果选择对称加密算法:
- 预装好
- 在双方通信的时候,协商好秘钥。那第一次和对方沟通用什么秘钥时只能明文发送,以后再用这个就没有意义了,所以采用对称加密是绝对不行的。
-
如果选择非对称方式
client进行请求,server将公钥返回给client端,让client端对数据进行加密并将加密之后的数据传过来,server就用原来的私钥进行解密,从而得到数据。
在此过程中,公钥是全世界都可以获取的,但是私钥只有server有。
但是server将处理之后的结果进行返回时,是不安全的,所以,只有一对秘钥和私钥只能保证数据单向的安全性。
-
所以如果各自有自己的公钥和私钥,在通信的阶段提前交换双方的公钥不就能保证双向的安全了吗?理论是。
事实并非如此!
1.依然会有被非法窃取的风险
2.非对称算法特别费时间。
所以纯的对称和纯的非对称方案都不好。
-
-
那实际是怎么做的呢? 对称加密+非对称加密
client进行请求,server发出公钥S,client先形成S的对称秘钥的一种私钥X,S再对X进行加密得到X+传给server,server用S’得到x。数据传回的时候采用对称方案进行数据的加密的加密和解密。x只有双方知道。X就是秘钥协商阶段(非对称算法)得到的。
秘钥协商阶段用非对称的方式实现对称数据通信交互时的对称加密秘钥X。
-
server给公钥S的时候,可不可能出现问题呢?
安全不是让别人拿不到,而是别人拿到了也没办法处理。(解密的成本远大于解密之后的收益)
如果有个中间人,采用抓包系统半路截胡获取到了公开的服务器端提供的公钥S,用自身的公钥M交给了client,那么client就像前面一样,根据M形成自己的私钥X,并且用M将X进行加密,形成M+(X)想返回给服务器,但是服务器只认识S,所以M+又被中间人获取,中间人通过私钥M’对M+进行解密,得到了客户端的私钥X,同时将服务器的公钥S加密X返回给服务器,这样就狸猫换太子般的得到了服务器和客户端之间进行对称加密的私钥X,就可以在日后的信息交互中获取信息。
其中client并不知道秘钥协商报文是不是合法服务器发来的。
-
那么如何处理这种中间人的存在呢?如何让客户端能够区分到底哪个是服务端提供的公钥呢?
就产生了一个机构,CA证书机构(有自己的公钥和私钥),通过给服务器颁发证书,证明在公钥协商阶段,哪个公钥是服务端的,中间人还改不了的。服务器申请CA证书需要提供自己的基本信息和域名公钥等一串文本信息。
-
过程大致如下:
公司给CA机构发送自己的文本个人信息(域名+公钥S等),CA机构创建证书,CA将文本信息经过哈希散列的方式形成数据摘要(指纹),也就是一段唯一的字符序列,再用自己独一无二的私钥对摘要进行加密形成数据签名,最后将数据签名和公司提交的文本内容形成的证书一并返回给公司。
那么之后,client端发送请求,server端直接发送证书(内置自己的公钥S),即便中间商获取,用CA机构公开的公钥进行解密证书中的数字签名,修改对应的内容生成新的数据摘要和签名,但是由于没有CA机构加密的私钥,无法形成和CA机构一样加密形式的数据签名,即便修改了内容,客户端也能区分出来不一样,
客户端用CA机构的公钥将数据签名解密得到一份数据摘要,将整数中的文本进行哈希散列形成一份数据摘要,如果两份不一样就说明内容遭到了修改。
-
注:CA的公钥是全世界都知道的,但是私钥只有自己知道。只有CA机构能够形成对应的数字签名。数据签名和内容是对应的,只有他能用独一无二的私钥生成数据签名,跟你明牌了直接。
-
如果中间人也是一个合法的用户呢,用自己合法的证书替代server的证书呢?不能
因为证书当中有域名,就相当于你把服务器域名改了,谁信啊。
浏览器里面内置 了很多CA机构,公钥信息。安全性-管理证书:中间证书,收信人的中间机构啥的都是内置的等。但多数情况都是浏览器内置的,如果网站不合法,就会提醒你下载证书,但是结果自己承担。下图为火绒浏览器设置中的安全证书。