从一个HTTP请求来读懂HTTP、TCP协议

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"从一个HTTP请求来看网络分层原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"两台主机间会通过非常多网络设备,不管哪个网络设备都会发生数据丢失,如果发生数据丢失的话,会发生数据重传,会出现数据重复(之前丢失的包并不是丢失而是产生了延时)。数据传输的介质也可能多样,如内网里通过网线进行传输,连接到公网的话会通过光纤进行连接,所以要实现不同介质间信号的转换,还有从光纤到路由器无线脉冲转换,距离远的话还有信号衰减问题。所以在网络传输过程中有非常多的问题需要解决,把问题分组分层,不同层次间解决不同问题,不同层次间定义标准化接口让它们间可以进行数据的通信。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"复杂的网络"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/2ca881c90ac1021517ea89c524ddd692.png-wh_600x-s_379608925.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/898d19e0668f3b2d5ce640b5c293f186.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/2ca881c90ac1021517ea89c524ddd692.png-wh_600x-s_379608925.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了简化网络的复杂度,网络通信的不同方面被分解为多层次结构,每一层只与紧挨着的上层或者下层进行交互,将网络分层,这样就可以修改,甚至替换某一层的软件,只要层与层之间的接口保持不变,就不会影响到其他层。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"OSI( Open System Interconnection Reference Model): 开放系统互联参考模型"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP/IP 协议族"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"OSI七层理论体系结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/cce1692da335fa5d2dde2a8a30f93227.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e7/e723e4d74254b14ae715a21e7f3abdd6.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/cce1692da335fa5d2dde2a8a30f93227.png","title":null}}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"物理层:"},{"type":"text","text":"解决两台主机的通信问题—A往B发送比特流(0101),B能接收到这些比特流。定义了物理设备的标准如网线的类型,光纤的接口类型以及传输介质的传输速率等。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"数据链路层:由于物理层上的传输的比特流可能会出现错传、误传等,所以数据链路层定义了如何格式化数据即将比特流封装成帧,提供了错误检测。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"网络层:"},{"type":"text","text":"随着节点的增加,点对点通信是需要经过多个节点的,如何找到目标节点,如何找到最优路径变成为了首要需求。所以出现了网络层,主要目的是将网络地址翻译成对应的物理地址,分组传输、路由选择,本层的传输单位是数据报(分组),本层需要注意的TCP/IP协议中的TCP协议。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"传输层:"},{"type":"text","text":"随着网络需要的进一步扩大,通信过程中需要传输大量的数据,网络可能会发生中断,为了保证传输大量文件时的准确性,需要对发送的数据进行切分,切分成一个个的segment进行发送,考虑如何在接受方拼接切分的segment组成完整的数据,以及发现丢失segment时该如何处理,需要注意的协议TCP、UDP。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"会话层:"},{"type":"text","text":"不同机器上的用户之间建立以及管理会话。用于保证应用程序自动收发包和寻址。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"表示层:"},{"type":"text","text":"信息的语义语法,加密解密,转换翻译,压缩解压缩。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"应用层:"},{"type":"text","text":"规定双方必须使用固定长度的消息头,且消息头必须记录消息长度等信息。需要注意的是TCP/IP协议中的HTTP协议。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/7f70bcd8b566495c50709b65918b2708.png-wh_600x-s_3042196636.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/73/733b6578e18e8e4c3f4f1c3ee49d60c3.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/7f70bcd8b566495c50709b65918b2708.png-wh_600x-s_3042196636.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"TCP/IP四层模型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是OSI的一种实现,包括应用层、运输层、网际层和网络接口层。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/098fa5e287316c1fd52125b9112cb8e4.png-wh_600x-s_3103511319.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/76/76cb34519bd4344cb7fc86c2d0e7bc91.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/098fa5e287316c1fd52125b9112cb8e4.png-wh_600x-s_3103511319.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"一个HTTP请求的分层解析流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/eac5b8bcad9162d4ec75ccecf4c29ea8.png-wh_600x-s_3348559328.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f7/f752f69ead1f8d1e76e9e14bd55193c7.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/eac5b8bcad9162d4ec75ccecf4c29ea8.png-wh_600x-s_3348559328.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上图右边一个服务器部署了一个静态页面,通过nginx部署在公网上,浏览器通过域名对它进行访问,浏览器输入域名点回车后是怎么工作的呢?"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"http://www.dumain.com "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服务端只认ip地址,浏览器将域名解析出来,看下浏览器里有没有域名对应DNS的缓存,有的话直接拿到服务端的ip地址,没有的话去本地的host文件看有没有配置,没有配置的话才会发起一个DNS请求用来获取服务器ip地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS也是台服务器也有自己的ip地址,这时候应用层会构造一个DNS请求报文,应用层会去调用传输层的接口一个socket的API,DNS默认使用UDP实现数据传输,即应用层调用传输层的API,传输层会在DNS请求报文基础上加一个UDP的请求头,传输层将数据交给网络层,网络层同样在UDP请求报文基础上加IP的请求头,网络层会将IP请求报文交给数据链路层,数据链路层会将自己的mac头加上去并把对应的请求报文交给下一个机器的mac地址也会加上去,下一个机器的mac地址通过网络层ARP协议找到,ARP会发送一些请求看下你对应的ip地址的mac地址是多少,最后通过物理层物理介质传出去,通常传到路由器上."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"路由器是三层设备(从下向上)从物理层开始连接,物理层交给数据链路层,数据链路层看下地址是不是给我的,是给我的进行解析,不是给我的就丢弃,报文再传给上面一层网络层,网络层把数据传到下一个路由器的地址是多少,会通过运营商的网络接口传到运营商的路由器上,运营商有自己的DNS服务器,如果配置的是运营商自己的DNS服务器的话会直接在这个DNS服务器里找自己对应的域名拿到对应的ip地址,也就是刚请求DNS报文地址,然后原路返回解析直到应用层拿到刚域名对应的ip地址,这样就可以进行HTTP请求报文的发送,再调用传输层协议是TCP参数,同样每到一层加头。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HTTP"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"什么是HTTP?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HTTP特点"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"无状态:协议对客户端没有状态存储,对事物处理没有“记忆”能力,比如访问一个网站需要反复进行登录操作。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"无连接:HTTP/1.1之前,由于无状态特点,每次请求需要通过TCP三次握手四次挥手,和服务器重新建立连接。比如某个客户机在短时间多次请求同一个资源,服务器并不能区别是否已经响应过用户的请求,所以每次需要重新响应请求,需要耗费不必要的时间和流量。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"基于请求和响应:基本的特性,由客户端发起请求,服务端响应。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"简单快速、灵活。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"通信使用明文、请求和响应不会对通信方进行确认、无法保护数据的完整性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP协议版本已经演化到3.0版本,关于协议版本可以查看 快速掌握HTTP1.0 1.1 2.0 3.0的特点及其区别"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HTTP报文格式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"起始行(start line):描述请求或响应的基本信息"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"头部字段集合(header):使用 key-value 形式更详细地说明报文"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中起始行和头部的字段并成为 请求头 或者 响应头,统称为 Header;消息正文也叫实体,称为 body。HTTP 协议规定每次发送的报文必须要有 Header,但是可以没有 body,也就是说头信息是必须的,实体信息可以没有。而且在 header 和 body 之间必须要有一个空行(CRLF)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/1a566786f4f87b134c732c45cca957cb.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/72/729ca82aac598f042a848f9ba59293f9.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/1a566786f4f87b134c732c45cca957cb.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"请求行报文格式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/289db58a4e8ba25b6460fb0f44648815.png-wh_600x-s_2637844719.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f7/f71d705041c02d0b1887cd8d5fc19760.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/289db58a4e8ba25b6460fb0f44648815.png-wh_600x-s_2637844719.png","title":null}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请求方法:如 GET/HEAD/PUT/POST,表示对资源的操作;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请求目标:通常是一个 URI,标记了请求方法要操作的资源;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"版本号:表示报文使用的 HTTP 协议版本。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"响应报文格式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/2a016c6c4e954d755a429cc3edfdaba6.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/94/94c457bf22bcba348313c53c41493ede.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/2a016c6c4e954d755a429cc3edfdaba6.png","title":null}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"版本号:表示报文使用的 HTTP 协议版本;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"请求及响应报文格式对比"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s3.51cto.com/oss/202010/20/239df0ef71358728caeb8272490eb0bb.png-wh_600x-s_4035336112.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7d/7debbcca603827b57b4d432b5abf4d74.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s3.51cto.com/oss/202010/20/239df0ef71358728caeb8272490eb0bb.png-wh_600x-s_4035336112.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"HTTP 头字段"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"段结束。比如前后分离时经常遇到的要与后端协商传输数据的类型“Content-type: application/json”,这里 key 就是“Content-type”,value 就 是“application/json”。HTTP 头字段非常灵活,不仅可以使用标准里的 Host、 Connection 等已有头,也可以任意添加自定义头,这就给 HTTP 协议带来了无限的扩展可能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"头字段注意事项"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字段名不区分大小写,字段名里不允许出现空格,可以使用连字符“-”,但不"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"能使用下划线“_”(有的服务器不会解析带“_”的头字段)。字段名后面必须紧接 着“:”,不能有空格,而“:”后的字段值前可以有多个空格;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字段的顺序是没有意义的,可以任意排列不影响语义;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 协议中有非常多的头字段,但基本上可以分为四大类:通用标头、实体标头、请求标头、响应标头。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 头字段更多内容请查看《深入掌握HTTP四种标头基本概念 》"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"TCP 协议"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP(Transmission Control Protocol),传输控制协议:面向连接的,可靠的,基于字节流的传输层通信协议。它能帮助你确定计算机连接到 Internet 以及它们之间的数据传输。通过三次握手来建立 TCP 连接,三次握手就是用来启动和确认 TCP 连接的过程。一旦连接建立后,就可以发送数据了,当数据传输完成后,会通过关闭虚拟电路来断开连接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"TCP特点"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于连接的:数据传输之前需要建立连接"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全双工的:双向传输"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"字节流:不限制数据大小,打包成报文段,保证有序接收,重复报文自动丢弃"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"流量缓冲:解决双方处理能力的不匹配"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可靠的传输服务:保证可达,丢包时通过重发机制实现可靠性"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拥塞控制:防止网络出现恶性拥塞"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"TCP报文格式"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/7862a3d83660617994b727d4ca41ec3b.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"16位源端口/16位目的端口:"},{"type":"text","text":"负责实现应用程序之间的数据传输"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"32位序号/32位确认序号:"},{"type":"text","text":"用于实现tcp在传输层的包序管理——tcp有序交付数据"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"4位头部长度:"},{"type":"text","text":"以4个字节为单位;4位保存的最大数字是15;因此tcp报头最大长度是15*4=60个字节"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"6位保留位;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"6位标志:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"URG——紧急指针标志"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ACK——确认回复标志"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"PSH——提示立即接受位"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RST——重置连接位"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"SYN——连接建立请求位"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"FIN——断开连接请求位"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"16位窗口大小:"},{"type":"text","text":"滑动窗口机制–>流量控制–>告诉对端所能发送的最大数据量"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"校验和:"},{"type":"text","text":"二进制反码求和–>校验数据一致性"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"紧急指针:"},{"type":"text","text":"指明哪些数据是紧急数据"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"选项数据:"},{"type":"text","text":"三次握手时,协商MSS大小的数据"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP连接:四元组[ 源地址, 源端口, 目的地址, 目的端口 ]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"TCP三次握手"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步通信双方初始序列号( ISN, initial sequence number )"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"协商TCP通信参数(MSS, 窗口信息,指定校验和算法)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在了解具体流程之前,我们先认识几个概念 "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/54464d86da72c012c1f981d1978cf73c.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最初两端的TCP进程都处于CLOSED关闭状态,A主动打开连接,而B被动打开连接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A、B关闭状态CLOSED — B收听状态LISTEN — A同步已发送状态SYN-SENT — B同步收到状态SYN-RCVD— A、B连接已建立状态ESTABLISHED"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求。然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求。若有,则作出响应。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"SYN:"},{"type":"text","text":"它的全称是 Synchronize Sequence Numbers ,同步序列编号。是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立 TCP 连接时,首先会发送的一个信号。客户端在发送 SYN 消息时,就会在自己的段内生成一个随机值 X。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"SYN-ACK:"},{"type":"text","text":"服务器收到 SYN 后,应答客户端连接,发送一个 SYN-ACK作为答复。确认号设置为比接收到的序列号多一个,即 X + 1,服务器为数据包选择的序列号是另一个随机数 Y。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"ACK:"},{"type":"text","text":" Ackowledge character ,确认字符,表示发来的数据已确认接收无误。最后客户端将 ACK 发送给服务器。序列号被设置为所接收的确认值即 Y+ 1。 "}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/58/58c0a55095934162bc7e603721060170.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面通过一个案例看三次握手是怎么进行的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Nginx服务器部署一个静态页面(我的端口为:8000)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s5.51cto.com/oss/202010/20/b33c078e3438bedf379fc11555c71777.png-wh_600x-s_652675029.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/81f6f6d73abc8a8b8dde071ff303c581.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s5.51cto.com/oss/202010/20/b33c078e3438bedf379fc11555c71777.png-wh_600x-s_652675029.png","title":null}},{"type":"text","text":"  "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"tcpdump指定网卡进行监听抓取报文"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"tcpdump -i en0 -S -c 3 port 8000 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在客户端使用nc网络工具发送一个请求"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"nc 192.168.109.200 8000 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"三次握手监听结果如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/250321025bce0f30c37d401a6f168fb0.png-wh_600x-s_1745764668.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/ca15db083f27b26375c2da7cf9ce02b3.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/250321025bce0f30c37d401a6f168fb0.png-wh_600x-s_1745764668.png","title":null}},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"内核在三次握手做的一些事情,如下:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8a/8a5bbf5077c3a41065e79354ffd5c422.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"连接状态查看"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"netstat -tpn # t:TCP连接装,p:进程显示 ,n:数字形式  # 每秒查看一次 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"netstat -tpn -c 1"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/4eebbb5d4c1df852206917abd65a3155.png-wh_600x-s_3237275751.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ca/ca15db083f27b26375c2da7cf9ce02b3.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s2.51cto.com/oss/202010/20/4eebbb5d4c1df852206917abd65a3155.png-wh_600x-s_3237275751.png","title":null}},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP四次挥手"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/82431c09da98b1e18fcd78d3c741ee1d.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f4/f4199b9e21d0430f2bff734a5080db2a.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s4.51cto.com/oss/202010/20/82431c09da98b1e18fcd78d3c741ee1d.png","title":null}},{"type":"text","text":" "}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A: 发送FIN数据包,代表A不再发送数据"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"B: 收到请求,开始应答 ,避免了A重新发送FIN重试(应答机制)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"B: 处理完数据之后关闭,关闭连接,及发送FIN请求"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A: 收到请求后发送ACK应答,B服务可以释放连接"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"等待 2MSL后释放连接"}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"防止报文丢失,导致B重复发送FIN"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"防止滞留在网络中的报文,对新建立的连接造成数据扰乱"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"字节流的协议"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TCP把应用交付的数据仅仅看成是一连串的无结构的字节流,TCP并不 知道字节流的含义,TCP并不关心应用程序一次将多大的报文发送到 TCP的缓存中,而是根据对方给出的窗口值和当前网络拥堵的程度来决 定一个报文段应该包含多少个字节。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"MSS: Max Segment Size, 默认 536byte 实际数据 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/6a32cd810c727c3d7a48ad34605a4927.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/77f3549184238d5b62c30b0e3b497acf.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/6a32cd810c727c3d7a48ad34605a4927.png","title":null}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在网络传输过程中可能会出现以下的一些情况:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客户端一段时间没有收到 ack 消息则重传"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果缓冲区满了则可能丢包或延时都需要重传"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根据报文 sequeence number 字段重排序,还需要丢弃重复包。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"数据传输的可靠性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"停止等待协议如下: "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s3.51cto.com/oss/202010/20/3d92bc12b5c0a1681c07a7e83e38c0bf.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/77f3549184238d5b62c30b0e3b497acf.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s3.51cto.com/oss/202010/20/3d92bc12b5c0a1681c07a7e83e38c0bf.png","title":null}},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"停止等待协议,效率比较低"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"重传机制如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ack 报文丢失"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d5/d5a3acdf585df89055dec51c6e2d8dd3.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请求报文丢失"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f8/f84c5a0f47952b1ae629f5a81879cf3d.png","alt":"从一个HTTP请求来读懂HTTP、TCP协议","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"滑动窗口协议与累计确认(延时ack)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上效率低,所以tcp提出了新的协议-滑动窗口协议与累计确认(延时ack)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"滑动窗口大小同通过tcp三次握手和对端协商,且受网络状况影响。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面是一个一个报文,实际可发一批报文,服务器并不是挨个去确认,上面回一个ack浪费资源,单独响应一个报文时,tcp本身一个报文至少20个字节再加上ip头报文20字节,所以一个ack至少40字节。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以延时ack的发送,如下图确认最后一个报文如5就可以,但这样也有一个问题如3的报文丢了,这时只能确认1和2连续报文,从3以后的报文全要重传,已确认的报文在缓冲区丢弃掉。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://s6.51cto.com/oss/202010/20/cf6792bf21872507abdce42c1d5e9ffc.png","title":null}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e1/e1d94e25d65006fac26794493198094b.png","alt":"","title":"","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:一角钱技术"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来源:https://www.toutiao.com/i6885366707725009419/"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章