因特网
因特网是世界范围的计算机网络,即它互联了全球数十亿计算设备(叫主机或者端系统)的网络。下图可作为因特网全貌结构。
端系统通过通信链路与分组交换机连接到一起。当端系统要向另一台端系统发数据时,会将数据分段,并为每段数据加上首部字节,而这种分段的数据术语称为分组。
- 通信链路:常见的有铜线、光纤等,不同的物理媒体组成其传输的速度自然不同。
- 分组交换机:有2种最著名的类型,路由器与链路层交换机。
端系统通过ISP(Internet Service Provider)接入网络。
端系统与分组交换机都要运行一系列的网络协议,这些协议控制数据在网络上的传输,因特网的主要协议统称为TCP/IP协议。
分组交换机
分组交换
- 分组:从源端系统向目的端系统发送一个报文,源将长报文划分为较小的数据块,称之为分组;
- 分组交换:在源和目的端系统之间,每个分组都通过通信链路和分组交换机,由分组交换机实现分组交换功能,转发分组至指定的通信链路;
例:如果某源端系统或分组交换机经过一条链路发送一个L比特的分组,链路的传输速率为R比特/秒,则传输该分组的时间为L/R秒。
存储转发传输
存储转发传输是指在交换机能够开始向输出链路传输该分组的第一个比特之前,必须接受到整个分组;
例:两个端系统经一条路由器构成的简单网络:
源在时刻0开始传输,在时刻L/R秒,该路由器刚好接收到整个分组,并且朝着目的地向出链路开始传输分组(这里暂时先只考虑传输时延);
在时刻2L/R,路由器已经传输了整个分组,并且整个分组已经被目的地接收;
考虑一种情形:如果一旦比特到达,交换机就转发比特,而不等到其全部到达再转发,那么传输时延将会是多少呢?
虽然有两条通信链路,但由于此处我们只考虑传输时延而不考虑传播时延和处理时延,也就意味着这段时间和距离以及通信链路的段数没有关系,那么依然可以直接得到答案:L/R秒;
考虑一般情形:通过由N条速率均为R的链路组成的路径,此时在源于目的地之间有N-1台路由器,此时从源发送一个分组,端到端的时延为:d=N*L/R。
时延
分组从一台主机(源)出发,通过一系列路由器传输,在另一台主机(目的地)中结束它的历程。当分组从一个结点(主机或路由器)沿着这条路径到后继结点(主机或路由器),该分组在沿途的每个结点经受了几种不同类型的时延。这些时延最为重要的是结点处理时延(nodal processing delay)、排队时延(queuing delay)、传输时延(transmission delay)和传播时延(propagation delay),这些时延总体累加起来是结点总时延(total nodal delay)。
其中RTT(Round-Trip Time)往返时延在计算机网络中它是一个重要的性能指标,表示从发送端发送数据开始,到发送方收到来自接收方的确认(接收端收到数据后便立即发送确认),总共经历的时延。
下面将以一张图来解释这些时延类型:
(1)处理时延
检查分组首部和决定将该分组导向何处所需要的时间是处理时延的一部分。处理时延也能包括其他因素,如检查比特级别的差错所需要的时间,该差错出现在从上游结点向路由器A传输这些分组比特的过程中。高速路由器的处理时延通常是微秒或更低的数量级。在这种结点处理之后,路由器将该分组引向通往路由器B链路之前的队列。
(2)排队时延
在队列中,当分组在链路上等待传输时,它经受排队时延。一个特定分组的排队时延长度将取决于先期到达的正在排队等待向链路传输的分组数量。如果该队列是空的,并且当前没有其他分组正在传输,则该分组的排队时延为0。另一方面,如果流量很大,并且许多其他分组也在等待传输,该排队时延将很长。我们将很快看到,到达分组期待发现的分组数量是到达该队列的流量的强度和性质的函数。实际的排队时延可以是毫秒到微秒量级。
(3)传输时延
假定分组以先到先服务方式传输,这在分组交换网中是常见的方式,仅当所有已经到达的分组被传输后,才能传输刚到达的分组。用L比特表示该分组的长度,用R bps(即b/s)表示从路由器A到路由器B的链路传输速率。例如,对于一条10Mbps的以太网链路,速率R=10Mbps;对于100Mbps的以太网链路,速率R=100Mbps。传输时延是L/R。这是将所有分组的比特推(传输)向链路所需要的时间。实际的传输时延通常在毫秒到微秒量级。
(4)传播时延
一旦一个比特被推向链路,该比特需要向路由器B传播。从该链路的起点到路由器B传播所需要的时间是传播时延。该比特以该链路的传播速率传播。该传播速率取决于该链路的物理媒体(即光纤、双绞铜线等),其速率范围是2×108~3×108m/s,这等于或略小于光速。该传播时延等于两台路由器之间的距离除以传播速率。即传播时延是d/s,其中d是路由器A和路由器B之间的距离,s是该链路的传播速率。一旦该分组的最后一个比特传播到结点B,该比特及前面的所有比特被存储于路由器B。整个过程将随着路由器B执行转发而持续下去。在广域网中,传播时延为毫秒量级。
排队时延和分组丢失
结点时延的最为复杂和有趣的成分是排队时延,所以详说一下。
每台分组交换机有多条链路与之相连,对于每条相连的链路,该分组交换机具有一个输出缓存(也称输出队列),用于存储路由器准备发往那条链路的分组。
如果到达输出缓存的分组需要传输到某条链路,但却发现那条链路正忙于传输其它链路(传输容量已满),该到达分组必须在输出缓存中排队等待,这段等待时间被称为排队时延。
又由于输出缓存的大小也是有限的,当一个分组到达其输出缓存时,如果该输出缓存已经被其它需要传输的分组完全充满了,那么此时就会出现丢包现象,到达的分组或者排队的分组之一会被丢弃。
例:如下图,假设A与B通过100Mbps以太网向E发送分组,当分组到达第一个路由器时,会将这些分组导向一个15Mbps的链路。在某个短时间间隔内,如果分组到达路由器的到达率(转换为每秒比特)超过的15Mbps,这些分组在通过链路传输之前将在输出缓存中排队,在第一个路由器中将会出现拥塞。
转发表和路由选择协议
- IP地址:在因特网中,每台端系统具有一个IP地址,用于唯一标识自己的身份,目的IP地址存在于源发送的分组首部,且该地址具有等级结构,想象一下按照邮政地址发送的方式,二者极其相似;
- 转发表:每台路由器都拥有一个转发表,用于将目的地址(或其一部分)映射成为输出链路;
这样一来,整个过程就一目了然了,当某分组到达一台路由器时,路由器检查该地址,并用这个目的地址搜索器转发表,并且将其转发给适当的通信链路;
- 路由选择协议:因特网中用于自动设置这些转发表的协议标准。
套接字
多数应用程序是由通信进程对组成的,每对进程向对方互发报文,这个过程是通过一个叫套接字(socket)的软件接口来实现发送与接收报文的,因此套接字也称为API(Application Programming Interface)。如果广泛地讲,就是客户端服务端就是通过自己的套接字进行通信,所以在创建客户端跟服务端时,首先是先创建套接字,而套接字会绑定端系统的一个端口,这样的话外部向端口传输数据时就是向绑定的套接字进行传输。
TCP的通信过程会有点特殊,服务端的套接字是分为2种的:欢迎套接字跟连接套接字。当三次握手未完成时,客户端是跟服务端的欢迎套接字进行通信的,也就是服务端的对外端口绑定的是欢迎套接字,三次握手成功后,服务端会新生成一个连接套接字,专门做后续的通信。
很明显在运输层是有一个功能是指引数据通向哪个套接字的功能,叫多路分解,而客户端封装数据通过套接字发送则叫多路复用。
协议栈
因特网的协议栈分为5层:
- 应用层:是网络应用程序与应用层协议存留的地方,协议一般包括HTTP(web请求)、SMTP(电子邮件)、 FTP(文件传输)、DNS(域名系统)。这一层的分组叫做报文。
- 运输层:负责封装拆解来传输报文,协议为TCP与UDP。这一层的分组叫做报文段。
- 网络层:运输层会传递报文段和目标地址给它。这一层的分组为数据报。
- 链路层:这一层的分组为帧。
- 物理层:这一层的分组会被转化成比特字节,通过电信号把数据传输下去。
分组在每一层的时候,都会被封装该层的首部数据,一般来说分组是由2部分组成:首部字段与有效载荷字段。
应用层
HTTP
HyperText Transfer Protocol, 中文称超文本传输协议,它是Web 核心,由客户端与服务端实现,通过交换HTTP报文进行会话。因为HTTP服务器不保存关于客户的任何信息,所以HTTP也叫无状态协议。
HTTP使用TCP作为它的运输层协议(此文不包含HTTP3.0),客户端首先发起一个与服务器的TCP连接,一旦连接成功就可以通过套接字接口访问TCP。
为了提高效率,HTTP默认是持续连接模式,就是在一定时间内相同客户端与服务端共用一个TCP连接。
报文段结构
请求报文
- 请求行:方法(GET/POST)+url+版本号(HTTP/(0.9/1.0/1.1/2))\r\n
- 首部行(header):请求的属性,‘:’分割的键值对,每组属性之间用\r\n进行间隔。
- 空行:\r\n; 协议头的结束标记
- 实体体(body):空行后面的内容都是body,body允许为空字符串,如果body存在,通常会在header中有一个 Content-Length的属性来记录body的长度
举个例子:
GET /test.html?name=test&password=123456 HTTP/1.1
Host: www.test.cn
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
响应报文
- 状态行:协议版本号+响应状态码(以1开头/以2开头/…3/…4/…5)—(200/302/400/404/502/500)+状态码解释
- 首部行(header):请求的属性,‘:’分割的键值对,每组属性之间用\r\n进行间隔。
- 空行:\r\n; 协议头的结束标记
- 实体体(body):空行后面的内容都是body,body允许为空字符串,如果body存在,通常会在header中有一个 Content-Length的属性来记录body的长度;如果服务器返回了一个HTML页面,那么HTML页面的内容就在body中。
举个例子:
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 20 Feb 2017 09:13:59 GMT
Content-Type: text/plain;charset=UTF-8
Vary: Accept-Encoding
Cache-Control: no-store
Pragrma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: no-cache
Content-Encoding: gzip
Transfer-Encoding: chunked
Proxy-Connection: Keep-alive
{"code":200,"msg":0}
常见的请求方法
方法 | 说明 |
---|---|
GET | 获取资源 |
POST | 传输实体主体 |
PATCH | 修改实体主体 |
PUT | 传输文件 |
DELETE | 删除文件 |
OPTIONS | 询问支持的方法 |
HEAD | 获得报文首部 |
TRACE | 追踪路径 |
CONNECT | 要求用隧道协议连接代理 |
LINK | 建立和资源之间的联系 |
UNLINK | 断开连接关系 |
常见的header
- Content-Type: 数据类型(text/html等)
- Content-Length: Body的⻓度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端⼝上;
- User-Agent: 声明⽤户的操作系统和浏览器版本信息;
- referer: 当前⻚⾯是从哪个⻚⾯跳转过来的;
- location: 搭配3xx状态码使用,告诉客户端接下来要去哪里访问;
- Cookie:用于在客户端存储少量信息,通常用于实现会话的功能。
[
](https://blog.csdn.net/weixin_44376490/article/details/99617782)
常见的状态码与说明
状态码 | 状态码解释 | 含义 |
---|---|---|
100 | Continue(继续) | 收到了请求的起始部分,客户端应该继续请求 |
101 | Switching Protocols(切换协议 | 服务器正根据客户端的指示将协议切换成Update Header列出的协议 |
200 | OK | 服务器成功处理了请求(这个是我们见到最多的) |
201 | Created(已创建) | 对于那些要服务器创建对象的请求来说,资源已创建完毕。 |
202 | Accepted(已接受) | 请求已接受, 但服务器尚未处理 |
203 | Non-Authoritative Information(非权威信息) | 服务器已将事务成功处理,只是实体Header包含的信息不是来自原始服务器,而是来自资源的副本。 |
204 | No Content(没有内容) | Response中包含一些Header和一个状态行, 但不包括实体的主题内容(没有response body) |
205 | Reset Content(重置内容) | 另一个主要用于浏览器的代码。意思是浏览器应该重置当前页面上所有的HTML表单。 |
206 | Partial Content(部分内容) | 部分请求成功 |
300 | Multiple Choices(多项选择) | 客户端请求了实际指向多个资源的URL。这个代码是和一个选项列表一起返回的,然后用户就可以选择他希望的选项了 |
301 | Moved Permanently(永久移除) | 请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置 |
302 | Found(已找到) | 与状态码301类似。但这里的移除是临时的。 客户端会使用Location中给出的URL,重新发送新的HTTP request |
303 | See Other(参见其他) | 类似302 |
304 | Not Modified(未修改) | 客户的缓存资源是最新的, 要客户端使用缓存 |
305 | Use Proxy(使用代理) | 必须通过代理访问资源, 代理的地址在Response 的Location中 |
306 | 未使用 | 这个状态码当前没使用 |
307 | Temporary Redirect(临时重定向 | 类似302 |
400 | Bad Request(坏请求) | 告诉客户端,它发送了一个错误的请求。 |
401 | Unauthorized(未授权) | 需要客户端对自己认证 |
402 | Payment Required(要求付款) | 这个状态还没被使用, 保留给将来用 |
403 | Forbidden(禁止) | 请求被服务器拒绝了 |
404 | Not Found(未找到) | 未找到资源 |
405 | Method Not Allowed(不允许使用的方法) | 不支持该Request的方法。 |
406 | Not Acceptable(无法接受) | |
407 | Proxy Authentication Required(要求进行代理认证) | 与状态码401类似, 用于需要进行认证的代理服务器 |
408 | Request Timeout(请求超时) | 如果客户端完成请求时花费的时间太长, 服务器可以回送这个状态码并关闭连接 |
409 | Conflict(冲突) | 发出的请求在资源上造成了一些冲突 |
410 | Gone(消失了) | 服务器曾经有这个资源,现在没有了, 与状态码404类似 |
411 | Length Required(要求长度指示) | 服务器要求在Request中包含Content-Length。 |
412 | Precondition Failed(先决条件失败) | |
413 | Request Entity Too Large(请求实体太大) | 客户端发送的实体主体部分比服务器能够或者希望处理的要大 |
414 | Request URI Too Long(请求URI太长) | 客户端发送的请求所携带的URL超过了服务器能够或者希望处理的长度 |
415 | Unsupported Media Type(不支持的媒体类型) | 服务器无法理解或不支持客户端所发送的实体的内容类型 |
416 | Requested Range Not Satisfiable(所请求的范围未得到满足) | |
417 | Expectation Failed(无法满足期望) | |
500 | Internal Server Error(内部服务器错误) | 服务器遇到一个错误,使其无法为请求提供服务 |
501 | Not Implemented(未实现) | 客户端发起的请求超出服务器的能力范围(比如,使用了服务器不支持的请求方法)时,使用此状态码。 |
502 | Bad Gateway(网关故障) | 代理使用的服务器遇到了上游的无效响应 |
503 | Service Unavailable(未提供此服务) | 服务器目前无法为请求提供服务,但过一段时间就可以恢复服务 |
504 | Gateway Timeout(网关超时) | 与状态吗408类似, 但是响应来自网关或代理,此网关或代理在等待另一台服务器的响应时出现了超时 |
505 | HTTP Version Not Supported(不支持的HTTP版本) | 服务器收到的请求使用了它不支持的HTTP协议版本。 有些服务器不支持HTTP早期的HTTP协议版本,也不支持太高的协议版本 |
cookie
HTTP是无状态的,而许多场景都是期望请求跟用户联系在一起,于是提出了cookie的概念,cookie技术有4个方面:
- HTTP请求报文有个cookie首部行,其键值为“Cookie”,提供服务端用户信息。
- HTTP响应报文有个cookie首部行,其键值为“Set-cookie”,提供给浏览器设值cookie。
- 在用户端系统保留一个cookie文件,并由用户的浏览器管理。
- 在服务端会有一个数据库存储cookie。
HTTPS
HTTP协议基于TCP进行传输的,其中传输的内容全都裸露在报文中,如果我们获取了一个HTTP消息体,那我们可以知道消息体中所有的内容。这其实存在很大的风险,如果HTTP消息体被劫持,那么整个传输过程将面临:
- 窃听风险(eavesdropping):通信使用明文(不加密),内容可能被窃听。
- 篡改风险(tampering):无法证明报文的完整性,所以可能遭篡改
- 冒充风险(pretending):不验证通信方的身份,因此有可能遭遇伪装
因此有了HTTPS,它是在HTTP通信中加入了SSL/TLS协议(当前版本是TLS1.2),通信的数据被加密了,防止被窃取,具体的通信流程如下:
- SSL:(Secure Socket Layer,安全套接字层),位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。
- TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
HTTPS的加密通信过程
- 客户端信任了服务端的证书,并和服务端确认了双方的加密算法【握手中需要的非对称算法、握手信息验证的hash算法、正文传输的对称加密】;
- 客户端生成随机数,通过证书中的公钥按照约定的非对称加密算法进行加密,得到加密的随机数秘钥,同时将之前所有的通信信息【秘钥算法套件、证书等所有的通信内容】按照约定的hash/摘要算法获取hash值,并使用随机数和协商好的对称加密算法进行签名加密,将随机数秘钥和加密签名发送到服务端。
- 服务端收到随机数秘钥和加密签名,先使用私钥将随机数按照约定的非对称解密算法进行解密,获取随机数,同时使用随机数按照约定的对称解密算法进行解密,获取待验证的hash值,将之前的通信消息体【秘钥算法套件、证书等所有的通信内容】按照约定的hash/摘要算法获取hash值,与刚才解密获取的待验证的hash值对比,验证加密成功与否。
- 成功以后,服务器再次将之前所有的通信信息【秘钥算法套件、证书等所有的通信内容】按照约定的hash/摘要算法获取hash值,并使用随机数和协商好的对称加密算法进行签名加密,将随机数秘钥发送到客户端,
- 客户端使用随机数按照约定的对称解密算法进行解密,获取待验证的hash值,将之前的通信消息体【秘钥算法套件、证书等所有的通信内容】按照约定的hash/摘要算法获取hash值,与刚才解密获取的待验证的hash值对比,验证加密成功与否,
- 成功的话整个链接过程完成,之后将使用随机数和约定的对称加密算法进行密文通信,【如果上面的任何步骤出现问题,都将会结束整个握手过程,导致建立安全连接失败】。
SMTP
是因特网电子邮件的核心,用于从发送方的邮件服务器发送报文到接收端的邮件服务器。
DNS
Domain Name System 域名系统。也可以叫做域名解析协议。在我们在浏览器访问网页的时候,通常度是用我们所熟悉的一连串有意义的英文字符标识,比如www.baidu.com、www.sohu.com等,但路由器并不能直接通过这些字符串去找到对应的路由器从而转发报文,所以就有了DNS协议,它的作用就是将域名解析成对应的IP地址,然后给路由器使用(实际上解析出来的IP地址是要封装到报文里的,也就是DNS协议是为其他协议服务的)。
为了解析域名,通常需要去按顺序访问不同的DNS服务器来获得IP地址,并且能够进行缓存到本地DNS服务器(一般保留2天),这样下次就无需再去遍历其他DNS服务器了,如下图所示:
运输层
UDP
User Datagram Protocol,为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。通俗的讲法是,UDP是一种无连接通信,不保证可靠性所以速度快。
报文段结构
如上图所示,头部只有四个字段,分别占用2个字节,其中检验和是比较难理解的。检验和是UDP做差错检测用,那它是怎么运作的呢?
首先发送方的UDP会对报文段的所有16比特的字段(也就是除了检验和其他三个字段)的和进行反码运算,在求和过程中有溢出的话都会进行回卷(溢出位与剩余位进行相加),得到的结果会放到检验和中。举个例子:
1000000000000000、1000000000000000、1000000000000000三个字段的值,第一个加第二个会得到17位的
10000000000000000,溢出16位,于是把17位的跟前16位的相加 1 + 0000000000000000 = 0000000000000001,再加上最后一个字段1000000000000001,进行反码01111111111111110,填入检验和。
这样的话,接收方把这四个字段相加正常的话就是16个1,有0的话说明在传输过程发生了问题。
TCP
Transmission Control Protocol 是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。
TCP被称为面向连接,是因为在发送数据之前,客户端与服务端必须进行相互握手,确保建立一条稳定可靠的通信通道。在进行握手的过程中,会确定一个参数MSS(Maximum Segment Size,最大报文段长度),顾名思义限定每次发送的报文段的长度。对于IPv4,为了避免IP分片,主机一般默认MSS为536字节,同理,IPv6的主机默认MSS为1220字节(一般情况下MSS的值是以网卡的MTU【最大传输单元】计算出来的)
报文段结构
如上图所示,它是由首部字段与数据字段组成。比较难理解的分开讲。
序号
TCP把数据看作是一个无结构、有序的字节流,所以序号是针对这个字节流设定的,举个例子:假设主机A上的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流。主机A中的TCP将隐式的对数据流中的每一个字节编号。假定数据流由一个包含500000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0。如图所示,该TCP将为该数据流构建500个报文段,给第一个报文段分配序号0,第二个报文段分配序号1000,第三个报文段分配序号2000,以此类推。每一个序号被填入到相应TCP报文段首部的序号字段。
确认号
确认号是指接收方已经确认接收到确认号前面的所有数据并期望从发送方收到的下个序号,这样的话,发送方就可确认接收方是否正常接收了已发送的数据。
TCP协议中,接收方成功接收到数据后,会回复一个携带确认号的报文段。发送方在一定时间内没有收到接收方携带确认号的报文段后,就会认为接收方没正常接收于是重新发送报文段。
接收方在接收到数据后,不是立即会给发送方发送携带确认号的报文段,而发送方发送报文段时,也并不是需要等上次发送被接收方确认了才可以继续发送下一个报文段。
接收方在收到数据后,有延迟确认机制,一般延迟确认的时间为200ms,系统有一个固定的定时器每隔200ms会来检查是否需要发送携带确认号的报文段。因为有了延迟确认时间,如果连续收到两个报文段,并不一定需要确认两次,只要回复最终的确认号就可以了,并且如果接收方有数据要发送,那么就会在发送数据的报文段里,带上确认号,就无需又以另一个单独的报文段来发送确认号,这么做可以大大降低网络流量。
首部长度
顾名思义,就是首部字段加起来的字节长度,有4位。注意的是首部长度就是固定以4个字节为单位,所以总是长度乘4才是最终结果。
保留未用
占6位,保留为今后使用,但目前应置为0 。
标志位 Flags
标志位,一共有 6 个,分别占 1 位,共 6 位 ,每一位分别表达不同意思。每一位的值只有 0 和 1,当为1时代表有效。
- CWR:通知对方已将拥塞窗口缩小。
- ECE:通知对方,从对方到这边的网络有阻塞。
- URG:紧急数据指针是否有效。紧急数据,期望优先处理,与首部的紧急数据指针配合使用。
- ACK: 确认号是否有效。TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1。
- PSH:当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置为1,并立即创建一个报文段发送出去。接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。
- RST:释放连接,然后再重新建立传输连接。
- SYN:在连接建立时用来同步序号。
- FIN:用来释放一个连接。
接收窗口
接收方会为每个连接分配一个接收缓存,就是RcvBuffer,在处理数据过程中,会把rwnd部分填充到接收窗口中来告知发送方自己的缓存空间来控制网络流量。
三次握手
- 客户端发送一个特殊的报文段,其标志位的SYN为1,并且随机选择一个序号(client_isn)当为序号发送出去。
- 服务端接收到该报文段后,会为连接分配缓存和变量,并且回馈发送方一个特殊的报文段,其标志位的SYN、ACK为1,并且随机选择一个序号(server_isn)当为序号,而确认号则为client_isn+1发送出去。
- 客户端接收到该报文段后,会为连接分配缓存和变量,并且回馈接收方一个特殊的报文段,其标志位的SYN为1,而确认号则为server_isn+1发送出去,并且这个报文段是可以携带真正想发的数据的。
三次步骤完成后,就保证建立了一条可靠的通信通道。
可以注意到的是,这里是有被攻击的风险的,当不法分子一直重复步骤1的话,会占用接收方大量的资源,叫SYN flood attack(syn洪泛攻击),而对应的有效防御措施则是SYN cookie,被部署在大多主流系统当中。
四次挥手
- 客户端发送一个特殊的报文段(连接释放),其标志位的FIN为1,并且随机选择一个序号(client_isn)当为序号发送出去。
- 服务端收到该报文段后,回馈报文段,其标志位的ACK为1,并且随机选择一个序号(server_isn)当为序号,而确认号则为client_isn+1发送出去。这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务端若发送数据,客户端依然要接受。
- 客户端收到服务端的确认报文段后,此时,客户端就进入 FIN-WAIT-2 状态,等待服务端发送连接释放报文段。
- 服务端将最后的数据发送完毕后,就向客户端发送连接释放报文段,其标志位FIN为1,并且随机选择一个序号(server_isn)当为序号发送出去。
- 客户端收到连接释放报文段后,回馈报文段,其标志位的ACK为1,并且随机选择一个序号(client_isn)当为序号,而确认号则为server_isn+1发送出去,此时客户端就进入了 TIME-WAIT 状态。注意此时TCP连接还没有释放,在2MSL(最长报文段寿命)的时间内未收到服务端的报文段后,进入 CLOSED 状态。
- 服务器只要收到了客户端发出的确认,立即进入 CLOSED 状态。
TIME-WAIT状态的问题
当连接并发量较高时,会产生大量的处于TIME-WAIT 状态的连接,每一个TIME-WAIT 状态,都会占用一个本地端口,上限为 65535(16 bit,2 Byte),这样的话新的连接就没有端口了导致创建失败,那怎么解决呢?修改对应文件并生效,简单来说,就是打开系统的TIMEWAIT重用和快速回收:
1、编辑内核文件/etc/sysctl.conf,加入以下内容:
# 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
# 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
# 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
# 修改系默认的 TIMEOUT 时间
net.ipv4.tcp_fin_timeout
2、然后执行 /sbin/sysctl -p 让参数生效。
3、如果效果还不明显,则可继续修改:
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.tcp_keepalive_time = 1200
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.ip_local_port_range = 1024 65000
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 8192
#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
# 默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于 Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。
net.ipv4.tcp_max_tw_buckets = 5000
连接状态
在一个TCP连接的生命周期内,运行在每台主机的TCP协议在各种TCP状态中变迁,其中客户端与服务端是有所差异的。客户端如下图:
服务端如下图:
拥塞控制
我们知道端与端是通过路由器来传递分组数据的,当分组数量过大已经超过路由器缓存的承载能力,这时候就得进行排队,一旦排队就会导致平均时延增高,也就会产生网络拥塞。
TCP所采用的方式就是让发送方根据感知到的网络拥塞情况来限制自我的发送速率,这里由三方面去分析。
发送方如何控制发送速率
TCP连接每一端都是由一个接收缓存、一个发送缓存和几个变量(lastByteRead、rwnd等)组成。运行在发送方的TCP拥塞控制机制跟踪一个额外的变量,即cwnd拥塞窗口(congestion window)。在一个发送方中未被确认的数据量不会超过cwnd与rwnd最小值,即:LastByteSent - LastByteAcked ≤ min(cwnd,rwnd),这样的话就可以控制发送的数据量来控制发送速率。
发送方怎么感知网络拥塞情况
- 发送方超时或收到接收方3个冗余ACK报文段,则发送方就认为在发送方到接收方的路径上出现了拥塞的指示。
如果发送方确认分组以高速率到达, 则该拥塞窗口将会更为迅速地增大,因为TCP使用确认来触发(或计时)增大它的拥塞窗口长度,这样的话为探测拥塞开始出现的速率,TCP发送方不断增加它的传输速率,看看拥塞开始速率是否发生了变化。TCP发送方的行为也许类似于要求(并得到)越来 越多糖果的孩子,直到最后告知他“不行!”,孩子后退一点,然后过一会儿再次开始提出请求。注意到网络中没有明确的拥塞状态信令,即ACK和丢包事件充当了隐式信号,并且每个TCP发送方根据异步于其他TCP发送方的本地信息而行动。
发送方采用什么算法来限制发送速率
发送方采用了TCP拥塞控制算法(TCPcongestion control algorithm),该算法包括3个主要部分:慢启动、拥塞避免、快速恢复。慢启动和拥塞避免是TCP的强制部分,两者的差异在于对收到的ACK做出反应时增加cwnd长度的方式。我们很快将会看到慢启动比拥塞避免能更快地增加cwnd的长度。快速恢复是推荐部分,对TCP发送方并非是必需的。
慢启动
当一条TCP连接开始时,cwnd的值通常初始置为一个MSS, 这就使得初始发送速率大约为MSS/RTT。由于对TCP发送方而言,可用带宽可能比MSS/RTT大得多,TCP发送方希望迅速找到可用带宽的数量。因此,在慢启动(slow-start) 状态,cwnd的值以1个MSS开始并且每当传输的报文段首次被确认就增加1个MSS在图3 50所示的例子中,TCP向网络发送 第一个报文段并等待一个确认。当该确认到 达时,TCP发送方将拥塞窗口增加一个MSS,并发送出两个最大长度的报文段。这两个报文段被确认,则发送方对每个确认报文段将拥塞窗口增加一个MSS,使得拥塞窗口变为4个MSS,并这样下去。这一过程每过一个RTT,发送速率就翻番。因此,TCP发送速率起始慢,但在慢启动阶段以指数增长,如图所示:
显然报文段肯定是不能以这种速度无限增长的,那什么时候制止这种指数继续增长呢?这里有2个方面去制止:如果存在一个由超时指示的丢包事件(即拥塞),发送方将第二个状态变量的值ssthresh(慢启动阈值)设置为cwnd/2 ,即当检测到拥塞时将ssthresh置为拥塞窗口值的一半,并将cwnd设置为1,然后重新开始慢启动过程。
- 第二种方式是直接与ssthresh的值相关联。因为当检测到拥塞时ssthresh设为cwnd的值一半,当到达或超过ssthresh的值时,继续使cwnd翻倍可能有些鲁莽。因此,当cwnd的值等于ssthresh时,结束慢启动并且转移到拥塞避免模式。
最后一种方式是,如果检测到3个冗余ACK,这时TCP执行一种快速重传(在该报文段的定时器过期之前重传丢失的报文段)并进入快速恢复状态。
拥塞避免
一旦进入拥塞避免模式,cwnd的值大约是上次遇到拥塞时的值的一半,即距离拥塞可能并不遥远!因此,TCP无法每过一个RTT再将cwnd的值翻番,而是采用了一种较为保守的方法,当发送正常时每个RTT只将cwnd的值增加一个MSS。
但是何时应当结束拥塞避免的线性增长呢?当出现超时时,与慢启动的情况一样,cwnd的值被设置为1个MSS,ssthresh的值被更新为cwnd值的一半,然后继续线性增长。
- 如果检测到3个冗余ACK,发送方将ssthresh的值记录为cwnd的值的一半,并且将cwnd的值减半,然后进入快速恢复模式。
快速恢复
在快速恢复模式中,对于引起TCP进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK, cwnd的值增加一个MSS。最终,当对丢失报文段的一个ACK到达时,TCP在将cwnd减半后后进入拥塞避免状态。
如果出现超时事件,快速恢复会将cwnd的值被设置为1个MSS,并且ssthresh的值设置为 cwnd值的一半,然后进入到慢启动模式。
网络层
网络层的作用从表面上看极为简单,即将分组从一台发送主机移动到一台接收主机。为此,需要两种重要的网络层功能:
- 转发:当一个分组到达路由器的一条输入链路时,路由器必须将该分组移动到适当的输出链路。
- 路由选择:当分组从发送方流向接收方时,网络层必须决定这些分组所采用的路由或路径。计算这些路径的算法被称为路由选择算法( routing algorithm)。
每台路由器具有一张转发表( forwarding table)。路由器通过检查到达分组首部字段的值来转发分组,然后使用该值在该路由器的转发表中索引查询。存储在转发表项中的该首部的值指出了该分组将被转发的路由器的输出链路接口。分组首部中的该值可能是该分组的目的地址或该分组所属连接的指示,这取决于网络层协议。
路由选择算法决定了插入路由器的转发表中的值。路由选择算法可能是集中式的,或是分布式的。在任何一种情况下,都是路由器接收路由选择协议报文,该信息被用于配置其转发表。通过考虑网络中的一种假想情况(不真实的,但技术上是可行的),其中所有的转发表是由人类网络操作员直接配置而在路由器中物理地存在,转发和路由选择功能的区别和不同能被进一步说明。在这种情况下,不需要任何路由选择协议!
IPv4
报文段结构
- 版本(号)。这4比特规定了数据报的IP协议版本。通过查看版本号,路由器能够确定如何解释P数据报的剩余部分。不同的P版本使用不同的数据报格式。
- 首部长度。因为一个IP4数据报可包含一些可变数量的选项(这些选项包括在IP4数据报首部中),故需要用这4比特来确定P数据报中数据部分实际从哪里开始。大多数P数据报不包含选项,所以一般的P数据报具有20字节的首部。服务类型。服务类型(TOS)比特包含在IPv4首部中,以便使不同类型的P数据报(例如,一些特别要求低时延、高吞吐量或可靠性的数据报)能相互区别开来。
- 数据报长度。这是P数据报的总长度(首部加上数据),以字节计。因为该字段长为16比特,所以P数据报的理论最大长度为65535字节。然而,数据报很少有超过1500字节的。
- 标识、标志、片偏移。新版本的IP(即IPv6)不允许在路由器上对分组分片。
- 寿命。寿命(Time-To-Live,TTL)字段用来确保数据报不会永远(如由于长时间的路由选择环路)在网络中循环。每当数据报由一台路由器处理时,该字段的值减1。若TL字段减为0,则该数据报必须丢弃。
- 协议。该字段仅在一个P数据报到达其最终目的地才会有用。该字段值指示了IP数据报的数据部分应交给哪个特定的运输层协议。注意在P数据报中的协议号所起的作用,类似于运输层报文段中端口号字段所起的作用。协议号是将网络层与运输层绑定到一起的粘合剂,而端口号是将运输层和应用层绑定到一起的粘合剂。
- 首部检验和。首部检验和用于帮助路由器检测收到的P数据报中的比特错误。首部检验和是这样计算的:将首部中的每2个字节当作一个数,用反码运算对这些数求和。,该和的反码(被称为因特网检验和)存放在检验和字段中。路由器要对每个收到的P数据报计算其首部检验和,如果数据报首部中携带的检验和与计算得到的检验和不一致,则检测出是个差错。路由器一般会丢弃检测出错误的数据报。注意到在每台路由器上必须重新计算检验和并再次存放到原处,因为TL字段以及可能的选项字段会改变。RFC1071。重复检测有几种原因。首先,注意到在P层只对P首部计算了检验和,而 TCP/UDP检验和是对整个TCP/UDP报文段进行的。其次,TCP/UDP与P不一定都必须属于同一个协议栈。原则上TCP能运行在一个不同的协议(如ATM)上,而IP能够携带不一定要传递给TCP/UDP的数据。
- 源和目的P地址。当某源生成一个数据报时,它在源IP字段中插入它的P地址,在目的IP地址字段中插入其最终目的地的地址。通常源主机通过DNS査找来决定目的地址。
- 选项。选项字段允许P首部被扩展。首部选项意味着很少使用,因此决定对每个数据报首部不包括选项字段中的信息,这样能够节约开销。然而,选项的可能存在的确是件复杂的事,因为数据报头长度可变,故不能预先确定数据字段从何处开始。而且还因为有些数据报要求处理选项,而有些数据报则不要求,故导致台路由器处理一个IP数据报所需的时间变化很大。这些考虑对于高性能路由器和
- 主机上的IP处理来说特别重要。由于这样或那样的原因,在IP6首部中已去掉了IP选项。
- 数据(有效載荷)。我们来看看最后的也是最重要的字段,这是数据报存在的首要理由!在大多数情况下,IP数据报中的数据字段包含要交付给目的地的运输层报文段(TCP或UDP)。然而,该数据字段也可承载其他类型的数据,如ICMP报文。
IPv6
报文段结构
- 版本。该4比特字段用于标识IP版本号。毫不奇怪,IP6将该字段值设为6。注意到将该字段值置为4并不能创建一个合法的IPv4数据报。
- 流量类型。该8比特字段与我们在IPv4中看到的TOS字段的含义相似。
- 流标签。该20比特的字段用于标识一条数据报的流。
- 有效載荷长度。该16比特值作为一个无符号整数,给出了IPv6数据报中跟在定长的40字节数据报首部后面的字节数量。
- 下一个首部。该字段标识数据报中的内容(数据字段)需要交付给哪个协议(如TCP或UDP)。该字段使用与Pv4首部中协议字段相同的值。
- 跳限制。转发数据报的每台路由器将对该字段的内容减1。如果跳限制计数到达时,则该数据报将被丢弃。
- 源地址和目的地址。IPv6128比特地址的各种格式在RFC4291中进行了描述。
- 数据。这是IPv6数据报的有效载荷部分。当数据报到达目的地时,该有效载荷就从IP数据报中移出,并交给在下一个首部字段中指定的协议处理。
[
](https://blog.csdn.net/u013553804/article/details/86494657)
ARP
一台网络设备要发送数据给另外一台网络设时,必须要知道对方的IP地址。但是,仅有IP地址是不够的,因为IP数据报文必须封装成帧才能通过数据链路层进行发送,而数据帧必须要包含目的MAC地址,因此发送端还必须获取到目的MAC地址。每一个网络设备在数据封装前都需要获取下一跳的MAC地址。IP地址由网络层来提供,MAC地址通过ARP协议来获取。
路由器一般都有一个ARP缓存表(ARP Cache),ARP缓存用来存放IP地址和MAC地址的关联信息。在往下层传输报文时,设备会先查找ARP缓存表,如果缓存表中存在对方设备的MAC地址,则直接采用该MAC地址来封装帧,然后将帧发送出去。如果缓存表中不存在相应的信息,则通过发送ARP请求报文来获得它,获取到的IP地址和MAC地址的映射关系会被放入ARP缓存表中存放一段时间。在有效期内,路由器可以直接从这个表中查找目的MAC地址来进行数据封装。
ICMP
ICMP(因特网报文控制协议),用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP通常被认为是IP的一部分,因为它是在IP分组里的,但从架构体系上来说它位于IP之上。它有四个明显的特点:
- ICMP本身是网络层的一个协议,但是它的报文不是直接传送给数据链路层,而是要封装成IP数据报,然后在传送给数据链路层。
- 从协议体系上看,ICMP的差错和控制信息传输只是要解决IP协议可能出现的不可靠问题,它不具有普遍意义上的传输机制,它也不是传输层赖以存在的基础,它不能独立于IP协议而单独存在,因此把它归于IP协议的一个部分,而归于IP协议体系。
- ICMP协议的设计初衷是用于IP协议在执行过程中的出错报告,严格的说是路由器向源主机报告传输差错的原因。
- ICMP不能纠正差错,只能报告差错。
下图为ICMP的报文类型: