学习此文章需要有OSI七层模型基础知识
1 HTTP原理
HTTP
是一个无状态的协议。无状态是指客户机(Web 浏览器)和服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP
遵循请求(Request)/应答(Response)
模型。客户机(浏览器)向服务器发送请求,服务器处理请求并返回适当的应答。所有 HTTP
连接都被构造成一套请求和应答
1.1 HTTP协议与TCP/IP协议的关系
HTTP
的长连接和短连接本质上是TCP
长连接和短连接。HTTP
属于应用层协议,在传输层使用TCP
协议,在网络层使用IP协议
。IP协议
主要解决网络路由和寻址问题,TCP协议
主要解决如何在IP层
之上可靠的传递数据包,使在网络上的另一端收到发端发出的所有包,并且顺序与发出顺序一致。TCP有可靠,面向连接的特点。
1.2 无状态的HTTP协议
HTTP
协议是无状态的,指的是协议对于 事务处理
没有记忆能力,服务器不知道客户端是什么状态。也就是说,打开一个服务器上的网页和你之前打开这个服务器上的网页之间没有任何联系。HTTP
是一个无状态的面向连接的协议,无状态不代表HTTP
不能保持TCP
连接,更不能代表HTTP
使用的是UDP
协议(无连接)。
2 长短连接详解
2.1 简介
在HTTP/1.0
中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP
操作,就建立一次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML
或其他类型的 Web
页中包含有其他的Web
资源,如JavaScript文件
、图像文件
、CSS文件
等;当浏览器每遇到这样一个Web资源
,就会建立一个HTTP会话
。
但从 HTTP/1.1
起,默认使用长连接
,用以保持连接特性。使用长连接的HTTP
协议,会在响应头有加入这行代码:Connection:keep-alive
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP
数据的 TCP
连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive
不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。
HTTP
协议的长连接和短连接,实质上是TCP
协议的长连接和短连接
2.2 TCP短连接
我们模拟一下TCP
短连接的情况,client
向server
发起连接请求,server
接到请求,然后双方建立连接。client
向server
发送消息,server
回应client
,然后一次读写就完成了,这时候双方任何一个都可以发起close
操作,不过一般都是client
先发起 close
操作。为什么呢,一般的server
不会回复完client
后立即关闭连接的,当然不排除有特殊的情况。从上面的描述看,短连接一般只会在 client/server
间传递一次读写操作
短连接的优点是:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段
2.3 TCP长连接
模拟一下长连接的情况,client
向server
发起连接,server
接受client
连接,双方建立连接。Client
与server
完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。
首先说一下TCP/IP
详解上讲到的TCP
保活功能,保活功能主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而可以代表客户使用资源。如果客户已经消失,使得服务器上保留一个半开放的连接,而服务器又在等待来自客户端的数据,则服务器将应该等待客户端的数据,保活功能就是试图在服务器端检测到这种半开放的连接。
如果一个给定的连接在两小时内没有任何的动作,则服务器就向客户发一个探测报文段,客户主机必须处于以下4个状态之一:
- 客户主机依然正常运行,并从服务器可达。客户的TCP响应正常,而服务器也知道对方是正常的,服务器在两小时后将保活定时器复位。
- 客户主机已经崩溃,并且关闭或者正在重新启动。在任何一种情况下,客户的TCP都没有响应。服务端将不能收到对探测的响应,并在75秒后超时。服务器总共发送10个这样的探测 ,每个间隔75秒。如果服务器没有收到一个响应,它就认为客户主机已经关闭并终止连接。
- 客户主机崩溃并已经重新启动。服务器将收到一个对其保活探测的响应,这个响应是一个复位,使得服务器终止这个连接。
- 客户机正常运行,但是服务器不可达,这种情况与2类似,TCP能发现的就是没有收到探查的响应。
2.4 长连接短连接操作过程
短连接的操作步骤
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
长连接的操作步骤
建立连接——数据传输…(保持连接)…数据传输——关闭连接
2.5 长连接和短连接的优点和缺点
长连接可以省去较多的TCP
建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户来说,较适用长连接。不过这里存在一个问题,存活功能的探测周期太长,还有就是它只是探测TCP
连接的存活,属于比较斯文的做法,遇到恶意的连接时,保活功能就不够使了。
在长连接的应用场景下,client
端一般不会主动关闭它们之间的连接,Client
与server
之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server
早晚有扛不住的时候,这时候server
端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server
端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个客户端连累后端服务。
短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP
的建立和关闭操作上浪费时间和带宽。
长连接和短连接的产生在于client和server
采取的关闭策略,具体的应用场景采用具体的策略,没有十全十美的选择,只有合适的选择。
2.6 什么时候用长连接,短连接
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP
连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP
连接。
例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
而像WEB
网站的http
服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好
3 HTTP发送过程解析
3.1 概述
HTTP
协议是基于请求/响应
的运作方式:
-
建立连接
:连接的建立是浏览器通过创建套接字(Socket
)实现的。浏览器打开一个套接字并把它约束在一个端口上,就可以在该端口上写数据并通过网络向外传送 -
发送请求
:打开一个连接后,浏览器把请求消息送到服务器的端口上,完成提出请求动作 -
发送响应
: 服务器在处理完客户的请求之后,向浏览器发送响应消息 -
关闭连接
:客户和服务器双方都可以通过关闭套接字来结束TCP/IP对话
3.2 传输流程
- 地址解析
如用客户端浏览器请求这个页面:http://localhost.com:8080/index.htm
从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下:
协议名:http
主机名:localhost.com
端口:8080
对象路径:/index.htm13/04/2018 Page 165 of 283
在这一步,需要域名系统DNS
解析域名localhost.com
,得主机的 IP 地址 - 浏览器查找域名的IP地址,
DNS
查找过程如下:
浏览器缓存
– 浏览器会缓存DNS
记录一段时间。 有趣的是,操作系统没有告诉浏览器储存DNS
记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。
系统缓存
– 如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows
里是gethostbyname
)。这样便可获得系统缓存中的记录。
路由器缓存
– 接着,前面的查询请求发向路由器,它一般会有自己的DNS
缓存
ISP DNS 缓存
– 接下来要check
的就是ISP缓存DNS
的服务器。在这一般都能找到相应的缓存记录。
递归搜索
–ISP
的DNS服务器
从跟域名服务器开始进行递归搜索,从.com
顶级域名服务器到Facebook
的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了
DNS
有一点令人担忧,这就是像wikipedia.org 或者 facebook.com这样的整个域名看上去只是对应一个单独的IP地址。还好,有几种方法可以消除这个瓶颈:
循环 DNS
是DNS
查找时返回多个IP时的解决方案。举例来说,Facebook.com实际上就对应了四个IP地址。负载平衡器
是以一个特定IP地址进行侦听并将网络请求转发到集群服务器上的硬件设备。 一些大型的站点一般都会使用这种昂贵的高性能负载平衡器。地理 DNS
根据用户所处的地理位置,通过把域名映射到多个不同的IP地址提高可扩展性。这样不同的服务器不能够更新同步状态,但映射静态内容的话非常好。Anycast
是一个IP地址映射多个物理主机的路由技术。 美中不足,Anycast与TCP协议适应的不是很好,所以很少应用在那些方案中
- 封装
HTTP
请求数据包
把以上部分结合本机自己的信息,封装成一个HTTP
请求数据包 - 封装成
TCP
包并建立连接
封装成 TCP 包,建立 TCP 连接(TCP 的三次握手) - 客户机发送请求命
客户机发送请求命令: 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是 MIME 信息包括请求修饰符、客户机信息和可内容。 - 服务器响应
服务器接到请求后,给予相应的响应信息, 其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。 - 服务器关闭
TCP
连接
服务器关闭TCP
连接: 一般情况下,一旦Web
服务器向浏览器发送了请求数据,它就要关闭TCP
连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-alive
,TCP
连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
大致结构如下所示:
3.3 请求和响应
3.3.1 一般头字段
一般头字段可用于请求消息
和响应消息
-
Cache-Contro
: "max-age=10" -
Connection
: "close" -
Date
: "Tue, 11 Jul 2000 18:23:51 GMT" -
Pragma
: "no-cache" -
Trailer
: "Date" -
Transfer-Encoding
: "chunked" -
Upgrade
: "SHTTP/1.3" -
Via
: "HTTP/1.1 Proxy1, HTTP/1.1 Proxy2" -
Warning
: "112 Disconnected Operation"
3.3.2 请求报头域
常见的HTTP
请求报头域:
-
Accept
:"text/html, image/*",用于指定客户端接受哪些类型的信息。 -
Accept-Charset
: "iso8859-5",请求端在发送请求时的头部信息,这个标识的是当前客户端可以接受的字符编码(所谓字符编码就是对于可见或者不可见字符的编码方式) -
Accept-Encoding
:"gzip, compress",类似Accept
,但是它是用于指定可接受的内容编码,这个编码与字符编码不是一个该概念,(我们经常会用到压缩文件,将正常文件进行压缩就是一种利用文件的底层编码进行的再编码)
服务器在发个客户端数据的时候会将数据先根据请求数据中的Accept-Charset
进行编码,然后对数据进行压缩
,也就是说通过Accept-Encoding
中可以接受的压缩算法进行压缩,这会有效减小网络压力,同时也减小了对客户端的延时,因为段数据在服务器进行压缩所用的时间与在网络上传输的时间相比还是比较理想的
如果请求消息中没有设置这个域,服务器假定客户端对各种内容编码都可接受 -
Accept-Language
:"en, fr",类似于Accept
,但是它是用于指定一种自然语言。如果请求消息中没有设置这个域,服务器假定客户端对各种语言都可接受。 -
Authorization
: [credentials],授权信息,通常出现在对服务器发送的WWW-Authenticate
头的应答中 -
Expect
: "100-continue",发送一个请求, 包含一个Expect:100-continue
, 询问Server是否愿意接受数据,接收到Server返回的100-continue应答以后, 才把数据POST给Server -
Host
:主要用于指定被请求资源的Internet
主机和端口号,它通常是从HTTP URL
中提取出来的 -
From
: "xxx@xxxxcom" -
User-Agent
:"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)",允许客户端将它的操作系统浏览器和其他属性告诉服务器。 -
Connnection
:标明客户是否能够处理持续性HTTP
连接。持续性连接允许客户或者浏览器在单个socket
中读取多个文件,从而节省协商几个独立连接所需的开销。 -
Content-Encoding
: "gzip",来标识服务端压缩时所用的压缩方式,Accept-Encoding
用来标识客户端能够理解的内容编码方式。Content-Encoding
用来标识主体进行了何种方式的内容编码转换 -
Cookie
:向服务器返回cookie
,这些cookie
是之前由服务器发送给浏览器的:request.getCookies()
3.3.3 响应结构
HTTP
响应由三个部分组成:
-
状态码(Status Code)
:描述了响应的状态。可以用来检查是否成功的完成了请求。请求失败的情况下,状态码可用来找出失败的原因。如果Servlet
没有返回状态码,默认会返回成功的状态码HttpServletResponse.SC_OK
-
HTTP头部(HTTP Header)
:它们包含了更多关于响应的信息。比如:头部可以指定认为响应过期的过期日期,或者是指定用来给用户安全的传输实体内容的编码格式。如何在Serlet
中检索HTTP
的头部看这里 -
主体(Body)
:它包含了响应的内容。它可以包含HTML
代码,图片,等等。主体是由传输在HTTP
消息中紧跟在头部后面的数据字节组成的
响应头结构示例:
-
Range
: bytes=206-5513,指定一种度量单位和一个部分被请求资源的偏移范围。 -
Refener
: www.myweb.com/news/search.html,一种请求头标域,标明产生请求的初始资源。对于HTML表单,它包含此表单的Web页面的地址 -
Accept-Ranges
: "none",一个响应头标,它允许服务器指明:将在给定的偏移和长度处,为资源组成部分的接受请求。该头标的值被理解为请求范围的度量单位 -
Age
: "2147483648(2^31)",允许服务器规定自服务器生成该响应以来所经过的时间长度,以秒为单位。该头标主要用于缓存响应 -
ETag
: "b38b9-17dd-367c5dcd",一种实体头标,它向被发送的资源分派一个唯一的标识符。对于可以使用多种URL请求的资源,ETag可以用于确定实际被发送的资源是否为同一资源 -
Last-Modified
: "Tue, 11 Jul 2000 18:23:51 GMT",指定被请求资源上次被修改的日期和时间 -
Location
: "http://localhost/redirecttarget.asp",对于一个已经移动的资源,用于重定向请求者至另一个位置。与状态编码302(暂时移动)或者301(永久性移动)配合使用 -
Proxy-Authenticate
[challenge],类似于WWW-Authenticate
,便是有意请求只来自请求链(代理)的下一个服务器的认证 -
Retry-After
: "Tue, 11 Jul 2000 18:23:51 GMT" or "60",一种响应头标域,由服务器与状态编码503
(无法提供服务)配合发送,以标明再次请求之前应该等待多长时间,此时间即可以是一种日期,也可以是一种秒单位 -
Server
: "Microsoft-IIS/5.0",一种标明Web服务器软件及其版本号的头标 -
Vary
: "Date",一个响应头标,用于表示使用服务器驱动的协商从可用的响应表示中选择响应实体。例如:Vary: *
3.3.4 HTTP响应码
HTTP响应码:
-
1xx
:临时,请求收到,继续处理 -
2xx
:成功,行为被成功地接受、理解和采纳 -
3xx
:重定向,为了完成请求,必须进一步执行的动作 -
4xx
:客户端错误,请求包含语法错误或者请求无法实现 -
5xx
: 服务器端错误,服务器不能实现一种明显无效的请求
响应码 | 响应信息 |
---|---|
100 | 继续,请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分 |
101 | 分组交换协议,请求者已要求服务器切换协议,服务器已确认并准备切换 |
200 | OK, 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页 |
201 | 已创建,请求成功并且服务器创建了新的资源 |
202 | 已接受,服务器已接受请求,但尚未处理 |
203 | 非授权信息,服务器已成功处理了请求,但返回的信息可能来自另一来源 |
204 | 无内容,请求收到,但返回信息为空 |
205 | 重置内容,服务器完成了请求,用户代理必须复位当前已经浏览过的文件 |
206 | 部分内容,服务器已经完成了部分用户的GET请求 |
300 | 多种选择,请求的资源可在多处得到 |
301 | 永久重定向,浏览器会记住,比如说从123.cn——>456.cn,浏览器不会请求123.cn,直接请求456.cn了 |
302 | 临时重定向,浏览器不会记住,比如说从123.cn——>456.cn,浏览器每次都会先请求123.cn,然后再请求456.cn |
303 | 查看其他位置,请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码,建议客户访问其他URL或访问方式 |
304 | 未修改,自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容 |
305 | 使用代理,请求者只能使用代理访问请求的网页。请求的资源必须从服务器指定的地址得到 |
306 | 前一版本HTTP中使用的代码,现行版本中不再使用 |
307 | 暂时重定向,服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求 |
400 | 错误请求,如语法错误 |
401 | 未授权,请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应 |
402 | 保留有效ChargeTo头响应,要求付费 |
403 | 禁止访问 HTTP |
404 | 未找到,没有发现文件、查询或URl |
405 | 方法禁用,用户在Request-Line字段定义的方法不允许 |
406 | 不接受,无法使用请求的内容特性响应请求的网页 |
407 | 需要代理授权,类似401,用户必须首先在代理服务器上得到授权 |
408 | 请求超时,客户端没有在用户指定的时间内完成请求 |
409 | 冲突,服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息 |
410 | 已删除,如果请求的资源已永久删除,服务器就会返回此响应 |
411 | 需要有效长度, 服务器不接受不含有效内容长度标头字段的请求,服务器拒绝用户定义的Content-Length属性请求 |
412 | 未满足前提条件,服务器未满足请求者在请求中设置的其中一个前提条件 |
413 | 请求实体太大,请求的资源大于服务器允许的大小 |
414 | 请求URI太大,请求的资源URL长于服务器允许的长度 |
415 | 不支持的媒体类型,请求资源不支持请求项目格式 |
416 | 请求范围不符合要求,如果页面无法提供请求的范围,则服务器会返回此状态代码 |
417 | 未满足期望值,服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求长,失败的预期 |
500 | 内部服务器错误 |
501 | 尚未实施,服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码 |
502 | 网关错误, 服务器作为网关或代理,从上游服务器收到无效响应 |
503 | 不可用的服务,服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态 |
504 | 网关超时,服务器作为网关或代理,但是没有及时从上游服务器收到请求 |
505 | HTTP版本未被支持,服务器不支持请求中所用的 HTTP 协议版本 |
4 三次握手四次挥手
TCP
在传输之前会进行三次沟通,一般称为三次握手
,传完数据断开的时候要进行四次挥手
4.1 数据包说明
4.1.1 TCP数据包
数据包说明:
-
源端口号
(16 位):它(连同源主机 IP 地址)标识源主机的一个应用进程 -
目的端口号
(16 位):它(连同目的主机 IP 地址)标识目的主机的一个应用进程。这两个值加上 IP 报头中的源主机 IP 地址和目的主机 IP 地址确定唯一一个 TCP 连接 -
顺序号seq
(32 位): 用来标识从TCP
源端向TCP
目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则TCP
用顺序号对每个字节进行计数。序号是32bit
的无符号数, 序号到达2^32-1
后又从 0 开始。 当建立一个新的连接时,SYN
标志变1
,顺序号字段包含 由这个主机选择的 该连接的初始顺序号ISN (Initial Sequence Number )
-
确认号 ack
(32 位): 包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1
。 只有ACK
标志为 1 时确认序号字段才有效。
TCP
为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。 -
TCP
报头长度(4 位):给出报头中32bit
字的数目, 它实际上指明数据从哪里开始。 需要这个值是因为任选字段的长度是可变的。这个字段占4bit
,因此TCP
最多有 60 字节的首部。然而,没有任选字段,正常的长度是20字节
-
保留位
(6 位):保留给将来使用,目前必须置为 0 。 -
控制位
(control flags
, 6 位):在TCP
报头中有 6 个标志比特,它们中的多个可同时被设置为 1 。依次为:
URG
:为 1 表示紧急指针有效,为 0 则忽略紧急指针值。
ACK
:为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
PSH
:为 1 表示是带有PUSH
标志的数据, 指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
RST
: 用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个RST
为 1 的报文,那么一定发生了某些问题。
SYN
:同步序号, 为 1 表示连接请求,用于建立连接和使顺序号同步synchronize
FIN
: 用于释放连接,为 1 表示发送方已经没有数据发送了,即关闭本方数据流。 -
窗口大小
(16 位):数据字节数,表示从确认号开始,本报文的源方可以接收的字节数,即源方接收窗口大小。窗口大小是一个16bit
字段,因而窗口大小最大为65535
字节。 -
校验和
(16 位):此校验和是对整个的TCP
报文段, 包括TCP
头部和TCP
数据,以 16 位字进行计算所得。这是一个强制性的字段,一定是由发送端计算和存储, 并由接收端进行验证。 -
紧急指针
(16 位):只有当URG
标志置 1 时紧急指针才有效。TCP
的紧急方式是发送端向另一端发送紧急数据的一种方式 -
选项
:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size)
。每个连接方通常都在通信的第一个报文段(为建立连接而设置 SYN 标志的那个段)中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充位,使得报头长度成为整字数。 -
数据
:TCP
报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段
4.1.2 UDP数据包
-
源端口号
(Source Port
):这个字段占据UDP
报文头的前 16 位,通常包含发送数据报的应用程序所使用的UDP
端口。接收端的应用程序利用这个字段的值作为发送响应的目的地址。这个字段是可选项,有时不会设置源端口号。没有源端口号就默认为 0 ,通常用于不需要返回消息的通信中。 -
目标端口号
(Destination Port
): 表示接收端端口,字段长为 16 位。 -
长度
(Length
): 该字段占据 16 位,表示 UDP 数据报长度,包含 UDP 报文头和 UDP 数据长度。因为 UDP 报文头长度是 8 个字节,所以这个值最小为 8,最大长度为2 ^ 16 = 65535
字节(即报文长度为64K) -
校验和
(Checksum
):UDP
使用校验和来保证数据安全性,UDP
的校验和也提供了差错检测
功能,差错检测
用于校验报文段从源到目标主机的过程中,数据的完整性是否发生了改变
转载于:https://mp.weixin.qq.com/s/aAAZQh8r7eCsIR9GsSvauw
4.1.3 TCP和UDP差异
- UDP 没有所谓的序列号和确认号,所以不会对数据进行确认,数据丢失后也不会进行重传,所以 UDP 是一种不可靠的协议
- TCP具有高可靠性,确保传输数据的正确性,不出现丢失或乱序,传输数据量大,没有限制
- TCP相对于UDP速度慢一点,效率低,而且要求系统资源较多,每个连接都会占用系统的CPU、内存等硬件资源
- UDP速度快、操作简单、要求系统资源较少,传输数据少,理论64K
- UDP不可靠,可能会出现丢包、乱序、数据不完整
4.2 三次握手
4.2.1 三次握手定义
客户端向服务器发送建立连接请求
- 第一次握手:主机 A 发送位码为
syn=1
,随机产生seq number=1234567
的数据包到服务器,主机 B 由SYN=1
知道, A 要求建立联机; - 第二次握手:主 机 B 收 到 请 求 后 要 确 认 联 机 信 息 , 向 A 发 送
ack number=( 主 机 A 的seq+1),SYN=1,ACK=1
,随机产生seq=7654321
的包 - 第三次握手: 主机 A 收到后检查
ack number
是否正确,即第一次发送的seq number+1
,以及位码ACK
是否为 1,若正确, 主机 A 会再发送ack number=(主机 B 的 seq+1),ACK=1
,主机 B 收到后确认seq 值
与ACK=1
则连接建立成功
4.2.2 三次握手问题
4.2.2.1 问题引入分析
概括起来,是这两个问题:
-
问
:TCP
三次握手中,客户端收到的第二次握手中ack
确认号不是自己期望的,会发生什么?是直接丢弃 or 回RST
报文?
答
:回RST
报文 -
问
:什么情况下会收到不正确的 ack(第二次握手中的 ack) 呢
答
:当客户端发起多次SYN
报文,然后网络拥堵的情况下,旧的 SYN 报文
比新的 SYN 报文
早抵达服务端,此时服务端就会按照收到的旧的 SYN 报文
回复syn+ack
报文,而此报文的确认号并不是客户端期望收到的,于是客户端就会回RST
报文
假如 TCP
三次握手中,客户端收到的第二次握手中 ack
确认号不是自己期望的过程如下:
当客户端连续发送多次建立连接的 SYN
报文,然后在网络拥堵的情况,就会发生客户端收到不正确的 ack
的情况。具体过程如下:
- 客户端先发送了
SYN(seq = 90)
报文,但是被网络阻塞了,服务端并没有收到,接着客户端又重新发送了SYN(seq = 100)
报文,注意不是重传SYN
,重传的SYN
的序列号是一样的。 -
旧 SYN 报文
比最新的 SYN
报文早到达了服务端,那么此时服务端就会回一个SYN + ACK
报文给客户端,此报文的确认号是 91(90+1)。 - 客户端收到后,发行自己期望收到的确认号应该是 100+1,而不是 90 + 1,于是就会回
RST
报文。 - 服务端收到 RST 报文后,就会中止连接。
- 后续最新的 SYN 抵达了服务端后,客户端与服务端就可以正常的完成三次握手了。
上述中的旧 SYN 报文
称为历史连接
,TCP
使用三次握手建立连接的最主要原因就是防止历史连接
初始化了连接。
4.2.2.2 历史连接
如果是两次握手连接,就无法阻止历史连接
,那为什么TCP
两次握手为什么无法阻止历史连接呢?
先说结论,主要是因为在两次握手的情况下,被动发起方
没有中间状态给主动发起方
来阻止历史连接,导致被动发起方
可能建立一个历史连接,造成资源浪费。
假如在两次握手的情况下,被动发起方
在收到 SYN
报文后,就进入 ESTABLISHED
状态,意味着这时可以给对方发送数据给,但是主动发
起方此时还没有进入 ESTABLISHED
状态,假设这次是历史连接,主动发起方判断到此次连接为历史连接,那么就会回 RST
报文来断开连接,而被动发起方
在第一次握手的时候就进入 ESTABLISHED
状态,所以它可以发送数据的,但是它并不知道这个是历史连接,它只有在收到 RST
报文后,才会断开连接。
可以看到,上面这种场景下,被动发起方
在向主动发起方
发送数据前,并没有阻止掉历史连接,导致被动发起方
建立了一个历史连接,又白白发送了数据,妥妥地浪费了被动发起方
的资源。
因此,要解决这种现象,最好就是在被动发起方
发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手。
4.3 四次挥手
TCP
建立连接要进行三次握手,而断开连接要进行四次。这是由于 TCP
的半关闭造成的。因为 TCP
连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭。这个单方向的关闭就叫半关闭
。当一方完成它的数据发送任务,就发送一个 FIN
来向另一方通告将要终止这个方向的连接
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭
- 关闭客户端到服务器的连接:首先客户端 A 发送一个
FIN
,用来关闭客户到服务器的数据传送,然后等待服务器的确认。其中终止标志位FIN=1
,序列号seq=u
- 服务器收到这个
FIN
,它发回一个ACK
,确认号ack
为收到的序号加 1 - 关闭服务器到客户端的连接:也是发送一个
FIN
给客户端。 - 客户段收到
FIN
后,并发回一个ACK
报文确认,并将确认序号 seq
设置为收到序号加 1
主机 A 发送 FIN 后,进入终止等待状态, 服务器 B 收到主机 A 连接释放报文段后,就立即给主机 A 发送确认,然后服务器 B 就进入 close-wait
状态,此时 TCP
服务器进程就通知高层应用进程,因而从 A 到 B 的连接就释放了。此时是半关闭
状态。即 A 不可以发送给B,但是 B 可以发送给 A。此时,若 B 没有数据报要发送给 A 了,其应用进程就通知 TCP
释放连接,然后发送给 A 连接释放报文段,并等待确认。 A 发送确认后,进入 time-wait
注意
,此时 TCP
连接还没有释放掉,然后经过时间等待计时器设置的 2MSL
后, A 才进入到close状态
在四次挥手期间,服务端不接收报文而发送RST
报文给客户端,客户端收到RST
报文会报错(NoHttpResponseException
)
5 HTTP1.1协议八种方法
5.1 定义
http
协议请求由三部分组成,分别是:请求行
、消息报头
、请求正文
请求行以一个方法符号开头,以空格分开,后面跟着请求的URI
和协议的版本
,格式如下:
Method Request-URI HTTP-Version CRLF
HTTP/1.1
协议中共定义了八种方法(有时也叫动作
)来表明Request-URI
指定的资源的不同操作方式:
-
OPTIONS
返回服务器针对特定资源所支持的HTTP
请求方法。也可以利用向Web
服务器发送*
的请求来测试服务器的功能性 -
HEAD
向服务器索要与GET
请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。 -
GET
向特定的资源发出请求。注意:GET
方法不应当被用于产生副作用
的操作中 -
POST
向指定资源提交数据进行处理请求(例如提交表单
或者上传文件
)。数据被包含在请求体中。POST
请求可能会导致新的资源的建立和/或已有资源的修改 -
PUT
向指定资源位置上传其最新内容。 -
DELETE
请求服务器删除Request-URI
所标识的资源。 -
TRACE
回显服务器收到的请求,主要用于测试或诊断。 -
CONNECT
HTTP/1.1
协议中预留给能够将连接改为管道方式的代理服务器。
5.2 get和post提交区别
简单来说,本质上区别:
-
GET
产生 一个 TCP数据包 -
POST
产生 两个 TCP数据包
也就是说:
对于
GET
方式的请求,浏览器会把http header
和data
一并发送出去,服务器响应200
(返回数据);
而对于POST
,浏览器先发送header
,服务器响应100 continue
,浏览器再发送data
,服务器响应200 ok(返回数据)
详细区别:
数据携带上:
GET
方式:在URL
地址后附带的参数是有限制的,其数据容量通常不能超过1K
。
POST
方式:可以在请求的实体内容中向服务器发送数据,传送的数据量无限制。请求参数的位置上:
GET
方式:请求参数放在URL
地址后面,以?
的方式来进行拼接
POST
方式:请求参数放在HTTP
请求包中用途上:
GET
方式一般用来获取数据
POST
方式一般用来提交数据
首先是因为GET
方式携带的数据量比较小,无法带过去很大的数量
POST
方式提交的参数后台更加容易解析(使用POST
方式提交的中文数据,后台也更加容易解决)
GET
方式比POST
方式要快GET方式比POST方式要快
post
请求包含更多的请求头
因为post
需要在请求的body
部分包含数据,所以会多了几个数据描述部分的首部字段(如content-type
),这其实是微乎其微的。
post
在真正接受数据之前会先将请求头发送给服务器进行确认,然后才真正发送数据post请求的过程:
[1] 浏览器请求tcp连接(第一次握手)
[2] 服务器答应进行tcp连接(第二次握手)
[3] 浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
[4] 服务器返回100 continue响应
[5] 浏览器开始发送数据
[6] 服务器返回200 ok响应get请求的过程:
[1] 浏览器请求tcp连接(第一次握手)
[2] 服务器答应进行tcp连接(第二次握手)
[3] 浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
[4] 服务器返回200 ok响应
也就是说,目测get
的总耗是post
的2/3
左右
get
会将数据缓存起来,而post
不会
post
不能进行管道化传输
在FORM
提交的时候,如果不指定Method
,则默认为get
请求,Form
中提交的数据将会附加在url
之后,以?
分开与url
分开。字母数字字符原样发送,但空格转换为+
号,其它符号转换为%XX
, 其中XX
为该符号以16进制
表示的ASCII
(或ISOLatin-1
)值。get
请求请提交的数据放置在HTTP
请求协议头中,而post
提交的数据则 放在实体数据中;
get
方式提交的数据最多只能有1024
字节,而post
则没有此限制
在表单里使用post
和get
有什么区别
在Form
里面,可以使用post
也可以使用get
。它们都是method
的合法取值。但是,post
和get
方法在使用上至少有两点不同:
-
get
方法通过URL
请求来传递用户的输入。post
方法通过另外的形式。 -
get
方式的提交需要用Request.QueryString
来取得变量的值,而post
方式提交时,必须通过Request.Form
来访问提交的内容。