首页IT科技tcpip协议简介(TCP/IP协议)

tcpip协议简介(TCP/IP协议)

时间2025-05-01 06:39:25分类IT科技浏览3319
导读:✏️作者:银河罐头 📋系列专栏:JavaEE...

✏️作者:银河罐头

📋系列专栏:JavaEE

🌲“种一棵树最好的时间是十年前            ,其次是现在            ”

TCP/IP协议

应用层协议

自定义应用层协议

为啥要自定义协议?

当前的应用程序要解决的业务场景是错综复杂的                 ,不同的公司有不同的业务      ,不同的业务有不同的流程      ,业务复杂                 ,要用程序来解决这个复杂的业务            ,程序也就复杂了           。因此很难有一个通用的协议满足所有的业务需求                  。

怎样进行自定义协议?

1)结合需求      ,分析清楚请求响应(客户端服务器之间)要传递哪些信息

2)明确传递的信息是以什么样的格式来组织

可选的方案是很多的      。

约定好协议得格式内容之后                 ,客户端就能够按照这个格式构造数据并发送            ,服务器按照这个格式解析处理           。

约定的协议的内容(传递的信息)是和业务相关性非常大的                  。但是协议的数据组织的格式(传递的格式)和业务关系不大.

典型的用来组织数据的格式:

XML 标签化的数据组织格式      。使用标签来表示键值对,以及树形结构

开始标签和结束标签需要成对出现                 ,标签中间的部分就是标签的内容                 ,内容可以是数字,字符串            ,还可以嵌套放别的标签     。

HTML 是 XML 的特殊情况

XML中标签名字叫啥都是自定义的

json

上述 xml 和 json 都是按照文本的方式来组织的                 ,

优点是可读性好      ,用户不需要借助其他工具            ,肉眼能看懂                  。

缺点是效率不高                 ,占用较多的网络带宽            。

xml 要额外传很多标签      ,json 要额外传很多的key

protobuffer (谷歌)

二进制的表示数据的方式      ,针对上述的数据信息                 ,通过二进制的形式进行压缩表示了     。

特点是肉眼观察不了(二进制直接用记事本打开乱码)            ,但是占用空间小了      ,占用的带宽也就降低了                 。

DNS

域名解析系统

域名                 ,网址            。

eg:www.baidu.com

要访问网络上的服务器            ,需要 IP 地址。IP地址不好记,于是就使用一些简单的字符串来表示这个地址                 。

每个域名都对应了 1/N 个IP 地址                  。

最原始的做法                 ,是使用一个 hosts 文件

C:\Windows\System32\drivers\etc

像 hash 表一样                 ,建立了 IP 和域名之间的映射关系,当然现在打开 hosts 文件            ,基本都是空的                 , # 表示注释

当访问某个域名的时候      ,就自动请求下 DNS 服务器            ,DNS 就会进行查询                 ,将得到的结果(具体的 IP 地址)返回给你。

如果你电脑的 DNS 服务器配置的不对      ,或者 DNS 服务器挂了      ,此时就会出现一个典型的现象:QQ 能用                 ,但是网页打不开           。

当前要求网站的域名不能重复(保证唯一)                  。

全世界这么多网站            ,如何保证唯一呢?

一级域名      ,二级域名                 ,三级域名…

pic.sogou.com

.com(公司) 一级域名            ,类似的一级域名还有 org,cn                 ,us…

sogou 二级域名                 ,表示 搜狗这个公司

pic 三级域名

域名分级了,DNS 服务器也是分级了            ,有 一级域名 的 DNS 服务器                 ,还有 二级域名      ,三级域名…

一般常见的就是 三级            ,四级左右      。

www.

具体 www 算 三级还是 四级还是 五级                 ,看域名具体咋写的      ,

www 是"万维网"的缩写      ,我们现在使用的这个网络就是 万维网

传输层协议

端口号

端口号:数据库 mysql 默认端口 3306           。

端口号起到的效果就是区分一个主机上的具体的应用程序                  。

所以                 ,同一台主机上            ,一个端口号不能被多个进程绑定      。

使用 IP 地址来区分主机     。

eg :进程 A 绑定了 3306      ,此时进程 B 也尝试绑定 3306                 ,进程 B 绑定操作就会失败 (抛异常)

端口号是传输层协议的概念            ,TCP 和 UDP 协议的报头中都会包含 源端口 和 目的端口                  。

TCP 和 UDP 都是用 2 个字节,16 个 bit 位来表示端口号的                 ,一个端口号的取值范围:0 ~ 65535

最初学习 C 语言                 ,就得知道不同长度字节数表示的数据范围            。

1 个字节,8 bit , -128 ~ 127 128 就是 2 ^ 7, 127 就是 2 ^ 7 - 1

​ 0 ~ 255 255 就是 2 ^ 8 - 1

2 个字节 , 16 bit, -32768 ~ 32767 32768 就是 2 ^ 15

​ 0 ~ 65535 65535 就是 2 ^ 16 - 1

4 个字节            ,32 bit                 , -21亿 ~ 21亿

​ 0 ~ 42 亿 9 千万

但是自己写程序      ,绑定的端口号            ,得是从1024 起的     。

0 ~ 1023 这个范围的端口称为"知名端口号/具名端口号"                 ,这些端口号已经分配给了一些知名的广泛使用的应用程序了                 。

那么我写代码就非要指定 0 ~ 1023 的端口号      ,行不行呢 ?

1.先确定你这个端口号没有被别的程序绑定

2.你有管理员权限

1023 以下的端口      ,也不是完全不能用                 ,只是不建议            ,这些端口虽然被分配给了特定程序      ,但是这个程序是否在你电脑上运行着                 ,电脑上是否安装了这些程序都是不一定的            。

UDP协议

UDP 的特点:无连接            ,不可靠传输,面向数据报                 ,全双工。

不可靠传输:没有任何安全机制                 ,发送端发送数据报以后,如果因为网络故障该段无法发到对方            ,UDP协议层也不会 给应用层返回任何错误信息                 。

UDP协议端格式

UDP 协议报文结构                 ,这张图      ,任何一个计算机网络的教科书上都有            ,而且都是这么画的                 ,但是实际上这么画不够严谨(为了书排版方便      ,变形了)

UDP 就会把载荷数据(就是通过 UDP socket       ,也就是 send 方法拿来的数据)再在前面拼装上几个字节的报头,相当于字符串拼接(此处是二进制的                 ,不是文本的)

UDP 报头里包含了一些特定的属性            ,携带了一些重要的信息                  。不同的协议功能不同      ,报头中的属性信息不同。

对于 UDP 来说                 ,报头 一共 8 个字节            ,分成 4 个部分(每部分 2 字节)

UDP 报文长度

这个长度也是用 2 个字节表示的,2 个字节表示的范围是 0 ~ 65535(64 KB).

也就是说一个 UDP 数据报只能传输 64 KB 的数据           。

报文长度是整个报文的长度                 ,不是载荷长度                  。

如果 应用层数据报超过 64 KB                  ,怎么办?

方案 1: 就需要在应用层,通过代码的方式针对应用层数据报进行手动的分包            ,拆成多个包通过多个 UDP 数据报进行传输(本来是 send 一次                 ,现在需要 send 多次了)

方案2:不用 UDP 了      ,换成 TCP(TCP 没有这样的限制)

校验和

作用是验证传输的数据是否是正确的      。

网络传输            ,本质上就是光信号/电信号                 ,这些可能会受到一些物理环境的影响(电场/磁场/高能射线)      ,在这些干扰下可能会出现"比特翻转"的情况           。(1->0      ,0->1)

一旦数据变了                 ,对于数据的含义可能是致命的                  。

这种现象是客观存在的            ,不可避免      ,我们要做的就是及时识别出当前数据是否出现问题      。

校验和是针对数据内容进行一系列数学运算                 ,得到一个比较短的结果(2 个字节)     。

如果数据内容一定            ,校验和结果就一定;如果数据变了,得到的校验和结果就变了                  。

是否有这样的情况                 ,假设数据传输中出错了                 ,但是计算的 校验和 和之前的 校验和 恰好一样?

“要发射导弹                 ” => 0xaabb

“不要发射导弹      ” => 0xaabb

这个情况理论上存在,工程上出现这种情况的概率极小            ,忽略不计了            。

如果内容相同                 ,得到的校验和一定相同;

如果校验和相同      ,内容不一定相同(小概率事件)

实际网络传输过程中            ,往往是把数据的所有字节都参与生成校验和的运算                 ,这样任何一个字节出问题就都能发现了     。

针对网络传输的数据来说      ,生成校验和的算法有很多种                 。其中比较知名的有几个            。

CRC

循环冗余校验      ,把数据的每个字节                 ,循环往上累加            ,如果累加溢出了      ,高位就不要了。

好酸但是校验结果不理想                 ,如果数据同时变动了 2 个 bit 位            ,前一个字节少 1 后一个字节 多 1 这种,就会出现内容变了但是校验和没变这种情况                 。

MD5

MD5 不是简单的相加                 ,有一系列公式来进行更加复杂的数学运算                  。

MD5算法的特点:

1)定长:无论原始数据多长                 ,得到的 MD5都是固定长度(4/8字节)

2)冲突概率很小:原始数据哪怕只变动一个地方,算出的 MD5 都会差别很大(让 MD5 值更分散了)

3)不可逆:通过原始数据计算 MD5 很容易            ,通过 MD5 还原成原始数据很难                 ,理论上是不可实现的(计算极大)

MD5 的这些特点使得 MD5 作用更多了:校验和      ,作为计算 hash 值的方式            ,加密

SHA1

SHA1的特点和 MD5 相同                 ,区别是计算方式不同。

TCP协议

特点是有连接      ,可靠传输      ,面向字节流                 ,全双工           。

这里的可靠传输是 TCP 的内部机制            ,和编码关系不大                  。

TCP协议段格式 4位首部长度

TCP 报文 = TCP 报头(首部) header + TCP 载荷 payload/body

一个TCP 报头长度是可变的      ,不是像 UDP 报头固定是 8 个字节

选项

option (选项)                  , optional (可选的            ,可有可无的)

此处的选项是对 TCP 报文的一些属性进行解释说明的      。

首部长度描述了 TCP 报头有多长,选项之前的长度是固定的(20 字节)                 ,首部长度 - 20 = 选项长度

首部长度的单位是 4 字节                 ,

如果首部长度值是 5,表示整个 TCP 报头长度是 20 字节            ,没有选项;

如果首部长度值是 15                  ,表示整个 TCP 报头长度是 60 字节      ,选项长度是 40 字节

保留( 6 位)

reserved

C 语言中            ,有一类单词                 ,叫做关键字      ,除了关键字之外      ,还有一些单词                 ,叫做保留字           。

保留字就是现在还没使用            ,但是保不齐以后要是用      ,所以现在这占个位置你现在先别用

咱们进行程序开发的时候                 ,其中一个重点考虑的事情就是可扩展性            ,有些功能可能暂时不需要,保不齐未来需要

16 位检验和

和 UDP 校验和同理

TCP工作机制

TCP 是一个复杂的协议                 ,里面有很多机制                 ,这里主要讨论 TCP 提供的 10 个比较核心的机制                  。

确认应答(安全机制)

TCP 是可靠传输      。

可靠,不是说发送方 100% 能把消息发给接收方            ,而是尽可能把数据传过去                 ,发送方能知道自己的消息是发过去了还是丢了     。

确认应答是实现可靠传输的最核心机制                  。

举个栗子:

张三在追一个女生      ,有一天张三给女生发消息说"我请你吃饭好嘛?“女生回复他说"好啊      ”            。

当张三收到"好啊"的时候            ,说明张三发的消息已经顺利被女生看到了(没有丢包)

如果过了好久张三没收到女生的回应                 ,说明丢包了(也可能是已读不回doge)

这里女生回复的"好啊"就称为"应答报文"      ,也叫作 ack(acknowledge)

TCP 进行可靠性传输      ,最主要考的就是这个确认应答机制     。

A 给 B 发了个消息                 ,B 收到之后就会返回一个 应答报文(ACK)            ,此时 A 收到应答之后就知道刚才发的数据已经顺利到达 B 了                 。

考虑更复杂的情况(发送方发多条消息):

张三可能连续发 2 条消息      ,张三没等第 1 条消息的回应就发了第 2 条消息            。

网络上可能存在"后发先至"的情况                 ,这个情况下收到消息的顺序可能是有变数的。

由于"后发先至"            ,张三先收到了"滚",后收到了"好啊"                 。

很明显这里应答错乱导致出现歧义                  。

网络中数据的"先发后至"                 ,两个主机之间                 ,路线存在多条,数据报1和数据报2走的都是不同的路线            ,数据报1的转发路径上的交换机/路由器和数据报2也不一样                 ,有的转发速率快有的转发速率慢      ,此时这2 个数据报的到达顺序可能会有变数。

网络中的"后发先至"是客观存在的            ,因此应答报文到达的顺序也是存在变数的                 ,此时就需要考虑如何规避这种顺序错乱带来的歧义           。

解决办法是给传输的数据和应答报文都进行编号                  。

“后发先至                 ”:

eg: 结婚时候新郎的车队可能会后发先至

32位序号/确认序号

引入序号之后就不怕顺序错乱了      ,即使顺序上乱了      ,也可以通过序号来区分当前应答报文是针对哪个数据进行的      。

任何一条数据(包括应答报文)都是有序号的                 ,确认序号则是只有应答报文有(普通报文确认序号里的值无意义)           。

报文是否是应答报文            ,取决于6个标志位中的 ACK      ,如果 ACK 这个标志位为 1                  ,表示是应答报文            ,如果为 0 表示不是应答报文                  。

应答报文的序号仅仅是一个身份标识,不需要应答 应答报文                 ,要是应答 应答报文就无限套娃了…

TCP 的序号是按照字节来编写的      。

这一条数据是 1000 个字节                 ,假设是从 1 开始编号,此时第一个字节序号就是 1            ,第 2 个字节序号就是 2…但是由于这 1000 个字节都属于同一个 TCP 报文                 ,TCP 报头里就只记录当前的第1 个字节的序号      ,此处报头的序号写的是 1     。接下来发第 2 条数据            ,此时第 2 个TCP 数据报的第 1 个字节序号是 1001                 ,如果长度是 1000      ,此时最后一个字节序号是 2000      ,这1001~2000都属于一个 TCP 数据报                 ,那么这个TCP 数据报报头的序号就是 1001                  。

TCP 的字节的序号是依次累加的            ,这个依次累加的过程对于后一条数据的第 1 个字节序号就是上一条数据的最后一个字节序号再加 1             。每个 TCP 数据报报头只需要填写 TCP 数据 第 1 个字节的序号即可     。

TCP 知道了第 1 个字节的序号      ,再结合报文的长度就可以知道每个字节的序号                 。

确认序号的取值                 ,是收到的数据的最后一个字节 + 1            。

如上图            ,确认序号写的是 1001,就是在 1000的基础上 +1。

表示的含义:

< 1001的数据都已经确认收到了                 。 主机 A 接下来应该从 1001 这个序号开始继续发送

总结:TCP 的可靠传输                 ,最主要就是通过确认应答来保证的                 ,通过应答报文,就可以让发送方清楚地知道传输是否成功            ,进一步引入了序号和确认序号                 ,针对多组数据进行区分                  。

超时重传(安全机制)

前面讨论确认应答      ,只是讨论了顺利传输的情况。如果丢包了呢?

丢包            ,涉及 2 种情况:

1.发的数据丢了

2.返回的 ack 丢了

发送方看到的结果就是没有收到 ack                  ,不能区分是哪一种情况           。

丢包是一个概率性事件      ,通常情况下丢包的概率是很小的      ,因此如果重新发一遍这个数据还是有很大可能传输成功的                  。

因此 TCP 就引入了 重传机制      。

在丢包的时候                 ,重新传一遍数据           。

到底这次传输是丢包了            ,还是 ack 传的慢?

TCP 就引入了时间阈值                  。

发送方发出数据后      ,就会等待 ACK                 ,此时开始计时      。只要超过了这个时间            ,不管是丢包了还是 ack 在路上都视为是丢包了     。

比如寒假作业,没带等于没写                 ,虽然你写了                 ,你超过了这个规定提交时间就当做没写处理                  。

超时重传:超过一定时间还没响应就重新传输            。

这个超时时间具体是多少 ms ,因为这个时间是可配置的            ,不同系统上的默认值可能都存在差别     。

如果是 ack 丢了这种情况

对于主机 B 来说                 ,1~1000 就收到了 2 次      ,如果你发的这个数据是一个支付请求?(寄了)

TCP 对于这种重复数据的传输            ,是有去重处理的                 ,

TCP 存在一个"接收缓冲区"这样的存储空间(接收方操作系统内核里的一段内存)      ,

每个 TCP socket 对象都有一个接收缓冲区(其实也有一个发送缓冲区)

主机 B 收到主机 A 的数据

其实是 B 的网卡读到数据了      ,然后把这个数据放到 B 的对应 socket 的接收缓冲区(想象成阻塞队列)                 ,后续应用程序使用 getInputStream            ,进一步使用 read      ,从接收缓冲区里读数据                 。

在接收缓冲区里                 ,根据数据的序号            ,TCP 很容易识别当前接收缓冲区里的这 2 条数据是否是重复的,如果重复就把后来的数据丢弃                 ,保证 应用程序调用 read 读到的数据一定是不重复的            。

去重可以理解为是一个优先级队列                 ,有序队列。

前面说了,网络上传输的数据可能"后发先至", TCP 利用这个接收缓冲区            ,对收到的数据重新进行排序                 ,是应用程序 read 的数据都是有序的(保证和发送顺序一致)

根据序号排序      ,序号相同就去重

eg:结婚当天新郎的车队出现"后发先至"            ,然后车队都到了新娘家门口的时候要整队                 ,排序

这时候我们可以利用前面提到的序列号      ,就可以很容易做到去重的效果                 。

总结:由于去重和重新排序机制的存在      ,发送方只要发现 ACK 没有及时到达                 ,就会重传数据            ,即使重复了      ,即使数据乱了                 ,接收方都能去重和排序(依赖 TCP 报头的序号)                  。

重传的数据是否可能又丢包了呢?

有可能            ,超时重传可能传多次,如果重传好几次都没传过去                 ,此时再重传意义不大了。

从数学角度来理解:

假设一次传输丢包概率是 10%                 ,传输成功概率是 90%;

如果第1次传输丢包,第 2 次传输也丢包了            ,概率是 10% * 10% = 1%

如果 3 次都丢了                 ,概率是 0.1%

连续重传都丢包      ,此时的概率原则上讲是非常低的            ,如果这个情况真的出现了                 ,只能说明此时丢包概率远远不止 10% => 你当前网络出现重大故障

因此      ,重传达到一定次数时      ,就不会继续重传                 ,会认为网络出现故障           。

接下来 TCP 会尝试 重置连接(相当于断开重连)            ,如果重置还是失败就彻底断开连接了                  。

具体重传几次 N      ,也是可配置的                 ,不去研究

重传的时候            ,第 1 次重传和第 2 次重传,超时时间间隔不一样      。一般来说                 ,重传轮次越大                 ,超时时间间隔就越大           。超时时间越大,重传频率就越低                  。(因为重传次数越多            ,说明重传成功概率越低                 ,此时你重传的太快也是白浪费系统资源)

总结:可靠传输是 TCP 最核心的部分      ,TCP 的可靠传输就是通过 确认应答 + 超时重传来体现的      。其中确认应答描述的是传输顺利的情况;而超时重传描述的传输出现问题的情况     。

连接管理(安全机制)

什么是断开连接?

A 和 B 把自己存储的连接信息(数据结构)删除了            ,连接就断开了                  。

JDBC 是基于 TCP 建立的连接

连接(Connection)

链接(Link) 快捷方式                 ,比如电脑开始菜单的图标就是快捷方式      ,比如我想打开 QQ      ,QQ本来是 在 C 盘某个目录里                 ,我每次打开 QQ 都要到这个目录去找未免太麻烦所以就创建一个快捷方式            ,这个快捷方式就是一个文件      ,这个文件里保存了这个可执行文件的真实路径

管理:

就是描述了连接如何创建                 ,如何断开            。

TCP 三次握手和四次挥手

滑动窗口(效率机制)

确认应答            ,超时重传,连接管理                 ,都是给 TCP 可靠性提供的支持     。

引入了可靠性                 ,也付出了相应的"代价"(传输效率)

可靠性和效率是冲突的                 。

因此 UDP 虽然没有 可靠性,但是传输效率比 TCP 高

所以 TCP 在竭尽可能的提高传输效率(本质上是补救措施)            。再怎么提高            ,也不可能比 UDP 这种不考虑可靠性的效率高。

滑动窗口本质上是降低了确认应答                 ,等待 ACK 消耗的时间                 。

在进行 IO 操作的时候      ,其实时间成本主要是 2 个部分:

1.等

2.数据传输(数据拷贝)

大多数情况下            ,IO 操作消耗的时间大部分在"等"上面

举个栗子:

放寒假从学校回家                 ,如果两地比较远      ,坐飞机回家的情况下                  。

真正坐飞机的时间很短      ,但是因为机场一般离市区很远                 ,从学校到机场要花时间            ,到了机场后换登机牌            、托运行李                 、安检      、候机又要花上不少时间      ,飞机到达目的地                 ,取行李            ,从机场到家里又要花上不少时间。

这里真正坐飞机的时间就相当于 数据传输的时间,较短           。

而其他花在路上的时间                 ,等待时间                 ,比较长                  。

具体怎么缩短?

批量发送,批量等待            ,把多份等待时间合并成一份      。

滑动窗口的本质就是不等待的批量发送一组数据                 ,然后使用一份等待时间来等待一组数据的多个 ack           。

举个例子:

我打算去买肉夹馍      ,炒饭            ,烤肠                  。

方案一:先去买肉夹馍                 ,肉夹馍做好了      ,再去买炒饭      ,炒饭做好了                 ,再去买烤肠      。

方案二:先去买肉夹馍            ,不等了立即去买炒饭      ,也不等立即去买烤肠     。

方案一就是用了 3 份等待时间                 ,而方案二只用了 1 份等待时间

把不需要等待            ,就能直接发送数据的最大的量,叫做"窗口大小"                  。上面第 2张图里"窗口大小"就是 4000            。

当批量发送了 窗口大小的这些数据之后                 ,发送方就要等待 ack 了     。

啥时候往下发送?等待啥时候结束?

不是说等所有的 ack 都到达才往下发                 ,而是收到一条 ack 就往下发 1 条                 。(这样就让我们等待的 ack 始终都是 4 条)

在上述情况下,如果丢包了咋办?

ack 丢了

上述丢包重传的机制            ,叫做 “快速重传            ”(重传操作只重传了丢失的数据)

这个可以视为是 超时重传 机制在滑动窗口下的变形            。

如果当前传输数据密集                 ,按照滑动窗口的方式传输      ,此时就按照快速重传来处理丢包;

如果当前传输数据稀疏            ,不再按照滑动窗口的方式传输                 ,此时就按照之前的超时重传来处理丢包。

流量控制(安全机制)

是一种干预发送的窗口大小的机制                 。

滑动窗口      ,窗口越大      ,传输效率越高(一份时间等的 ack 越多)                  。

但是窗口也不能无限大

1.完全不等 ack                  , 可靠性难以保证

2.窗口太大也会消耗大量的系统资源

3.发送速度太快            ,接收方接收不过来      ,发了也白发                 ,接收缓冲区有上限。

接收方的处理能力            ,是一个重要的约束依据           。发送方发的速度不能超过接收方的处理能力                  。

流量控制就是,根据接收方的处理能力                 ,来协调发送方的发送速率      。

如何衡量接收方的处理能力?

一个量化的方法:计算接收方一秒钟能处理多少个字节…(这种方式实现起来有些困难)

更简单的办法:

直接看接收方接收缓冲区里剩余空间的大小           。

每次 A 给 B 发了个数据                 ,B 就需要算一下缓存区里剩余空间,然后把这个值            ,通过 ack 报文返回给 A                  。

A 就根据这个值来决定接下来发送的速率是多少(窗口大小是多少)

窗口大小 16 位                 ,是否意味着      ,窗口大小最大是 16 kb ?

不是!

TCP 为了让窗口更大            ,在选项部分引入了窗口扩展因子      。

比如窗口大小已经是 64 kb                 ,扩展因子里写了个 2      ,意思就是让 64 kb << 2 = 256kb

由于接收方缓存区剩余空间是一直在动态变化的      ,所以每次返回 ack 带的窗口大小都在变化                 ,发送方也要进行动态调整     。

当窗口大小为0            ,发送方就要暂停发送                  。暂停发送的等待过程中      ,会给 B 定期发送窗口探测报文                 ,这个报文不携带具体的业务数据            ,只是为了触发 ack 查询窗口大小            。

拥塞控制(安全机制)

流量控制和拥塞控制共同决定发送方的窗口大小是多少     。

流量控制考虑的是接收方的处理能力                 。

拥塞控制描述的是传输过程中中间节点的处理能力            。

木桶效应:一只水桶能装多少水取决于它最短的那块木板。

接收方的处理能力,好量化衡量                 。但是中间节点不好衡量                  。

设计 TCP 的大佬们                 ,想出了一个相当 天才的办法                 ,

既然不好直接量化,那么可以通过"实验"的方式            ,来测试出一个合适的量

拥塞窗口不是固定数值                 ,而是一直动态变化的。随着时间的推移      ,逐渐到达一个动态平衡的过程           。

这样既能够把问题解决            ,同时也能随着网络的动态变化而动态变化                  。

拥塞窗口和流量控制窗口                 ,共同决定了发送方实际的发送窗口      。(取拥塞窗口和流量控制窗口的较小值)

延时应答(效率机制)

就是在 滑动窗口的基础上      ,做点别的操作           。

收到数据之后      ,不是立即返回 ack                  ,而是稍微等会再返回                  。等待的时间里            ,接收方的应用程序就能把接收缓存区的数据给消费一些      ,此时剩余的空间就更大了      。

实际上延时应答采取的方式                 ,就是在滑动窗口下            ,ack 不再每一条数据都返回了,比如上图是隔一条返回一个 ack     。

实际上剩余空间大小                 ,既取决于发送方的发送                 ,也取决于接收方的处理                  。

捎带应答(效率机制)

也是提高效率的一种方式            。

在延时应答的基础之上引入的捎带应答     。

服务器客户端程序,最典型的模型就是            ,“一问一答      ”                 。业务的请求和响应            。

本来是不同时机                 ,在延时应答下      ,可能成为相同时机            ,就合并了。

而 TCP 的三次握手本身是相同时机一定会合并                 ,此处是有一定概率合并      ,此处和四次挥手的合并更像                 。

延时应答提高了合并的概率                  。

面向字节流

面向字节流      ,引入了一件麻烦事                 ,

粘包问题。

举个栗子:

接收缓冲区是把刚才收到的多个数据都放到一起了            ,应用程序 read 读取的时候      ,读到哪里才算是一个完整的应用层数据报呢?

由于 TCP 是面向字节流的                 ,可以一次读 1 个字节也可以一次读 N 个字节           。

这就导致一次读取的时候            ,可能读到的是半个应用层数据报,也可能是 1 个应用层数据报                 ,还可能是多个应用层数据报                  。

应用程序调用 read                 ,

如果 read 的是 7 个字节,此时正好读出的是 aaaaaaa            ,这是一个完整的应用层数据报;

如果 read 的是 8 个字节                 ,此时正好读出的是 aaaaaaab      ,这是"一个半"的应用层数据报;

如果 read 的是 6 个字节            ,此时正好读出的是 aaaaaa                 ,这是半个应用层数据报      。

在 TCP 层次      ,没有在 socket api 中告诉我们要读几个字节      ,具体怎么读都是人为操作           。

期望读到的是整个的应用层数据报                 ,后续才好处理                  。

解决方案是 :约定好应用层协议即可            ,尤其是明确应用层数据报和应用层数据报之间的界限      。

1.约定好分隔符

2.约定每个包的长度

二选一即可     。

异常情况

传输过程中出现了不可抗力                  。

1.进程崩溃

2.主机关机(按照正常流程关机)

3.主机掉电

4.网线断开

1 和 2 是一类      ,3 和 4 是一类            。

1.进程崩溃:进程没了                 ,对应的 PCB 没了            ,对应的 文件描述符表就释放了,相当于 socket.close()     。此时内核会继续完成四次挥手                 ,此时仍然是一个正常断开的过程                 。

2.主机关机:主机关机要先关闭进程                 ,然后才正式关机            。(关闭进程的过程中,也和上面一样触发四次挥手)

3.主机掉电:显然是来不及挥手了。

假设是接收方掉电了                 。发送方仍然在传数据            ,发完数据要等 ack , ack 等不到…超时重传                 ,再怎么重传还是收不到 ack                  。重传几次还没有应答      ,尝试重置 TCP 连接            ,(复位报文段…RST)。显然这个重置也会失败           。放弃连接                  。(单方面放弃)

如果是发送方掉电      。接收方发现没有数据了                 ,没数据是发送方挂了?还是发送方在组织语言要稍等会再发?接收方不知道      ,先等           。接收方需要周期性的给发送方发送一个消息(“心跳包                 ”)      ,确认下对方是否还工作正常                  。

心跳包(保活机制)

1.心跳是周期性的;

2.心跳如果没了就是挂了      。

心跳包是一个非常常见的机制                 ,分布式系统中还会涉及到心跳包

心跳包来确认通信双方是否工作正常     。

4.网线断开:发送方尝试重传            ,接收方尝试发心跳包                  。

TCP 是个非常复杂的协议      ,不仅仅是这 10 个特性                 ,这是 TCP 比较核心的特性            。

了解 TCP 更多的特性            ,应该去翻阅 RFC 文档,翻阅操作系统内核源码

RFC 9293: Transmission Control Protocol (TCP) (ietf.org)

UDP 和 TCP 对比

TCP 的优势在于                 ,可靠传输                 ,绝大部分场景中,都需要可靠传输     。

UDP 优势在于            ,更高效率                 ,如果某些场景对于性能要求更苛刻                 。

同一个机房内部      ,服务器之间通信            。网络结构相对简单            ,网络带宽比较充裕                 ,转发设备也是比较好的设备。整体丢包的可能性比较小                 。

UDP 还有一个优势      ,天然支持广播                  。

IP 地址中有一种特殊的地址      ,“广播 IP            ”

通过 UDP 往广播 IP 上发送数据                 ,此时该局域网内所有的设备都能够收到数据。

传输层的协议并非只有这 2 个           。除了 TCP             ,UDP 之外      ,还有一些其他的传输层协议                  。有的传输层协议                 ,属于能够专门为游戏场景量身打造的(可靠性 + 效率) , 典型的协议            ,以 KCP 为代表的一系列协议      。

网络层协议

在复杂的网络环境中确定一个合适的路径           。

1.地址管理

快递系统要想要建立起来,就需要把世界上的地址能够以一定的规范定义出来                  。

2.路由选择

规划路径

网络层的代表                 ,IP协议      。

IP协议

IP协议报头格式如下:

IPv4协议(v4 版本的意思)

4 位版本

此处的取值只有 2 个     。4 或 6                  。

IPv4                 ,IPv6

4 位首部长度

描述了 IP 报头多长(IP 报头是变长的)

此处的单位也是 4 字节            。

IP 报头有一个选项部分,是变长的            ,是可有可无的     。

8 位服务类型(TOS)

说是 8 位                 ,实际上只有 4 位有效      ,这 4 位中只有 1 位可以是 1            ,其他的都是 0                 。

4 位就表示 IP 协议的四种形态/4 种工作模式            。

最小延时                 ,最大吞吐量      ,最高可靠性      ,最小成本。

最小延时                 ,包裹会最快速度送达                 。

最大吞吐量            ,单位时间内你可以送更多的包裹                  。

最高可靠性      ,包裹丢的概率是最小的。(和 TCP 的可靠性不是一个概念)

最小成本                 ,让运费最低           。

实际开发中就可以根据需要            ,来切换 IP 的模式,来达到最优的效果                  。

16位总长度(字节数)

描述了一个 IP 数据报的长度      。(头 + 载荷)

这个长度减去 IP 报头长度                 ,剩下的就是载荷长度                 ,就是一个完整的 TCP/UDP 数据报长度           。

16 位总长度,是否意味着一个 IP 数据报            ,最多只能支持 64 KB?

确实有这个限制                 ,但是 IP 自身就支持对 包的拆分和组装                  。

举个栗子:

在淘宝上买了个床      ,快递公司怎么运?把床拆开            ,以散件的形式发过来                 ,送到了家里      ,有师傅上门组装      。

一个 IP 数据报携带的数据载荷太大了      ,超过了 64KB                 ,就会在网络层针对数据进行拆分            ,把一个数据拆分成多个IP 数据报      ,再分别发送                 ,接收方再重新组装     。

比如            ,发送方:把 100KB 的数据交给传输层(封装),传输层交给网络层(封装)                 ,网络层就把这个 100KB 的数据拆包                 ,比如拆成两份 64KB + 36KB                  。这两份再交给数据链路层,由以太网分装成两个数据帧            。接收方:数据链路层针对两个数据帧进行分用            ,得到两个 IP 数据包交给网络层                 ,网络层针对这俩 IP 数据包进行解析      ,把里面的载荷拼成一个            ,交给传输层

此处说是 64 KB                 ,实际上 IP 协议拆包不一定就按照64 KB为单位进行拆分      ,实际的单位往往更小(取决于数据链路层的情况)

8位生存时间(TTL)

一个数据包在网络上能够传输的最大时间     。

这个时间的单位不是秒      ,而是次数

一个数据报构造出来                 ,会有一个初始的 TTL 数值(比如32或者64或者128)这个包每次经过一个路由器转发 TTL - 1            ,如果一直减到 0 了      ,还没有到达目标                 ,此时就认为这个报永远都到不了了            ,就可以丢弃了                 。

8 位协议

描述了当前载荷部分内容是属于哪个协议的(TCP/UDP)

16 位首部检验和

此处只需要针对首部进行检验            。

载荷部分(TCP/UDP数据报)自身已经有校验和了。

如果校验和不一致,直接丢弃

32 位源 IP 地址

32 位目的 IP 地址

此处看到的 IP 地址是 32 位的整数                 ,

而日常见到的 IP 则是一串数字                  , eg:111.183.72.219

点分十进制:使用 3 个"."把32位四个字节的数字给分隔开,分成四个部分            ,每个部分分别使用0 - 255 十进制整数表示                 。

地址                 ,期望每个设备都不相同      ,但是32位数字只能表示42亿9千万个数字.

期望使用这个表示全世界上所有的上网设备                  。(智能手机            ,PC                 ,服务器      ,路由器      ,其他杂七杂八的网络设备                 ,物联网)

为了解决IP地址不够用的问题            ,想了很多办法。

动态分配IP地址      ,此时就可以省下一批 IP 地址                 ,这个方案没有从根本上增加 IP 地址            ,只是提高了利用率           。

NAT 网络地址转换                  。本质是使用一个 IP 代表一批设备,也能够大大提高 IP 地址的利用率      。使用端口号区分           。

在NAT的背景下                 ,就把 IP 地址分成两个大类:

1)内网 IP (私有 IP) 10.* 172.16.* - 172.31.* 192.168.*

2)外网 IP (公网IP) 剩下的是公网 IP

NAT要求                 ,公网 IP 必须是唯一的                  。私网 IP 可以在不同的局域网中重复出现      。

如果某个私网里的设备想访问公网设备,就需要对应的 NAT 设备(路由器)            ,把 IP 地址进行映射                 ,从而完成网络访问     。

反之      ,公网的设备            ,无法直接访问私网的设备                  。不同局域网的私网的设备没法直接相互访问            。

cmd - ipconfig

内网 IP 只要在局域网内部不重复即可                 ,不同局域网中则是允许重复的     。

111.183.72.219 这是一个外网 IP

这些接入运营商路由器的电脑去访问外部服务器      ,都会被路由器替换成路由器自己的外网 IP                  。

此时只要这个电脑是经过运营商路由器转发给服务器      ,服务器看到的源 IP 就都一样                 ,如果是多个电脑同时访问同一个服务器            ,服务器的响应就会先发给路由器      ,路由器根据这些电脑不同的端口号来区分                 ,决定发给哪个设备            。

因此            ,服务器能拿到的只是路由器的 IP 不能拿到我电脑的内网 IP ,如果我电脑不主动和服务器联系                 ,服务器就不知道我的端口                 ,从而无法主动找到我。

NAT机制能够有效的解决 IP 不够用的问题,但是带来的副作用就是网络环境更加复杂了                 。

IPv6 : 根本上解决了IP不够用的问题                  。

使用16字节表示IP地址            ,128位。

16字节表示的地址的个数:(42亿)^4

IPv6 虽然看起来非常好                 ,但是世界上仍然是以 NAT + IPv4 + 动态分配来进行网络组建的      ,真正使用 IPv6的地方非常非常少           。

因为 IPv6 贵            ,IPv6和IPv4不兼容                 ,如果你想要支持IPv6      ,就需要更换路由器等网络设备      ,花钱                 ,这个钱花的就没啥收益                  。因此大家都不愿意去升级      。

IPv6 中国已经普及80%            ,背后原因

地址管理

IP地址分为两个部分      ,网络号和主机号           。

网络号:标识网段                 ,保证相互连接的两个网段具有不同的标识;

主机号:标识主机            ,同一网段内,主机之间具有相同的网络号                 ,但是必须有不同的主机号                  。

一个 IP 地址前面从哪儿到哪儿是网络号                 ,后面从哪儿到哪儿是主机号,分界线是怎么定的?

有一个单独的概念            ,

子网掩码

网络号不一定就是前三个字节                 ,都是可以调整变化的      。

此处的 255.255.255.0 就是子网掩码     。

32位

1111 1111 1111 1111 1111 1111 0000 0000

左侧都是 1      ,右侧都是 0            ,1的部分就描述了 IP 有多少位是网络号                  。

对于家用设备来说                 ,子网掩码最常见的就是 255.255.255.0

将IP地址中的主机地址全部设为0      ,就成为了网络号      ,代表这个局域网;192.168.0.0

将IP地址中的主机地址全部设为1                 ,就成为了广播地址            ,用于给同一个链路中相互连接的所有 主机发送数据包;(192.168.0.255 使用udp往这个地址上发数据报,整个局域网所有设备都能收到)

127.*的IP地址用于本机环回(loop back)测试      ,通常是127.0.0.1

主机号为 1                 ,比如 192.168.0.1 通常是"网关 IP"(不绝对            ,都可以配置)

将 IP 地址和子网掩码进行“按位与”操作(二进制相同位,与操作                 ,两个都是1结果为1                 ,否则为0),得到的结果就是网络号            。 将子网掩码二进制按位取反            ,再与 IP 地址位与计算                 ,得到的就是主机号     。

路由选择

路由选择就是规划路径                 。

相当于你使用地图导航一下      ,由于网络环境非常复杂            ,任何一个节点(路由器)都是无法感知到网络环境的全貌            。一个路由器                 ,最多只认识它的一些"邻居"或者"邻居"的"邻居"      ,进行一个比较长的路径的转发就比较麻烦      ,需要一边走一边问。

路由选择                 ,核心思路是"问路"                 。每个路由器都会保存一定的周围设备的信息(路由表)            ,每次有一个 IP 数据报经过      ,路由器就需要匹配路由表                 ,看看接下来咋走            ,如果路由表上有匹配的项(该路由器认识路),直接按照要求走就行了;如果没有匹配的项                 ,路由器不认识路会提供一个默认路径                 ,大体方向是不会错的,沿着这个方向继续走            ,继续问                  。

每经过一个路由器问一次                 , TTL- 1      ,如果减到零了            ,还没到说明这个包就永远都到不了了                 ,这个地址没人知道了      ,就被丢弃了。

六度空间理论:你和任何一个陌生人之间所间隔的人不会超过六个      ,也就是说                 ,最多通过6个中间人你就能够认识任何一个陌生人           。

正常情况下            ,像 32 这样的 TTL 足以让我们把数据报转发给世界上任何一个地址                  。

数据链路层协议

以太网

以太网帧格式 目的地址      ,源地址

此处不是用 IP 地址 表示了                 ,而是用 mac 地址      。

mac 地址

物理地址           。这个是和 IP 地址完全独立的另外一套地址体系                  。

6 个字节            ,(比 IPv4 地址大很多)当前每个设备都会有唯一的 mac 地址      。不是动态分配的,而是网卡出厂的时候就被设置好了的     。

当前 IP 和 mac 是怎么相互配合的?

IP 用来描述整个传输过程的起点终点                 ,mac 则是用来描述两个相邻结点的起点终点                  。

类型 ARP 协议

通过这个协议                 ,让某个 路由器/交换机 能够认识局域网里的其他设备            。通过 ARP 协议会在 路由器/交换机 里建立出一个 表,这个表相当于一个 hash 表            ,能够建立出 IP 和 mac 之间的映射关系     。

MTU

MTU 是数据链路层的一个数据帧能够承载数据的最大长度                 。(载荷长度)

载荷具体多长                 ,和使用的物理介质有很大关系      ,也和数据链路层使用的协议有关系            。比如以太网协议            ,MTU 1500字节(这个和物理层有很大关系)

正是这个 MTU 引起了 IP 协议拆包组包                 ,IP 协议的分包组包通常不是根据 这个 IP 最大长度 64KB 来分的。大概率情况下      ,MTU 比 64 KB 小                 。

声明:本站所有文章      ,如无特殊说明或标注                 ,均为本站原创发布                  。任何个人或组织            ,在未征得本站同意时      ,禁止复制      、盗用                 、采集            、发布本站内容到任何网站      、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益                 ,可联系我们进行处理           。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
火车头采集app内容(火车头视频网站采集教程网站采集技巧,帮助你轻松获取资源) 小米路由器显示正常连不上网(小米路由器显示连接但是用不了网络)