接上次博客:JavaEE初阶(9)(网络编程基础、TCP传输控制协议和UDP-用户数据报协议:Socket套接字、UDP数据报套接字编程【服务端、客户端】、TCP流套接字编程【服务端、客户端】、翻译和字典)-CSDN博客
目录
再谈协议
应用层
自定义协议
XML(可扩展标记语言)
JSON(JavaScript Object Notation)
Protocol Buffers(protobuf)
HTTP协议(超文本传输协议)
传输层(重点!)
UDP(用户数据报协议)
TCP(传输控制协议)
源IP和目的IP
端口号
UDP 协议
校验和原理
UDP的校验和:
循环冗余检验
MD5/SHA1 算法:
基于UDP的应用层协议
TCP协议(重中之重!!!)
TCP的相关特性
确认应答(ACK)
超时重传
超时时间的动态计算(RTO)
连接管理机制(安全机制)
服务器端状态转换:
客户端状态转换:
滑动窗口(效率机制)
流量控制(安全机制)
拥塞控制(安全机制)
延迟应答 (Delayed ACK)
延迟应答的原理:
延迟应答的实施策略:
注意事项:
捎带应答(效率机制)
面向字节流——粘包问题与解决方法
TCP异常情况
TCP的心跳机制:
TCP的其他机制
TCP和UDP的对比
适用场景
网络层重点协议
IP协议
认识IP地址
子网与网段划分
解决IP地址不够用的问题
1. 动态分配IP地址
2. NAT机制(网络地址转换)
3、IPv6:这个问题的最终解!
路由选择
DNS——域名解析系统
域名系统 (DNS) 概述
DN访问就近镜像服务器问题
DNS是如何查询的?
几级域名概念:
数据链路层重点协议
认识以太网
以太网帧格式
MAC地址
认识MTU
MTU对IP、UDP和TCP协议的影响
MTU对IP协议的影响
MTU对UDP协议的影响
MTU对TCP协议的影响
ARP协议
ARP协议的作用
ARP协议的工作流程
协议在计算机通信中的重要性不可忽视,它是确保通信双方能够正确地理解和解释传输数据的关键。以下是关于为什么需要协议的一些重要观点:
标准化和一致性:协议定义了通信双方之间数据的格式、编码方式、消息结构和交互规则。通过协议,可以确保通信的一致性,不同的系统和应用程序可以遵循相同的标准,从而实现互操作性。
数据解析和处理:协议规定了数据的结构和字段,这有助于接收方准确地解析和处理数据。无论是请求还是响应,接收方都可以按照协议定义的方式来提取所需的信息。
安全性:协议还可以包括安全性相关的规定,如身份验证、数据加密和防止中间人攻击等。这些规定可以确保通信的安全性,防止数据泄露和恶意篡改。
错误处理:协议可以定义错误代码和错误处理方式,使通信双方能够适当地处理错误情况,例如,如果请求无法满足,可以返回适当的错误代码和消息。
对于UDP和TCP协议以及应用层自定义协议,它们都有自己的协议格式:
UDP协议:UDP是一个简单的传输层协议,它主要定义了数据包的格式和传输方式。UDP数据包包括源端口、目标端口、数据长度和数据部分。UDP协议不提供可靠性和流控制,适用于那些不要求严格可靠性的应用。
TCP协议:TCP是一个可靠的传输层协议,它定义了数据包的格式、连接建立和关闭过程、数据流的管理、流控制和错误处理等。TCP协议确保了数据的可靠性和顺序传输。
自定义应用层协议:自定义应用层协议是根据特定应用的需求定义的协议。它们可以有不同的数据格式,包括请求和响应的消息结构,字段定义以及可能的错误处理机制。自定义协议允许应用程序根据自身需求来定义通信规则。
总之,协议是计算机通信中的基础,它确保了数据在传输和解释过程中的一致性和可靠性。选择合适的协议取决于应用程序的需求,可以使用现有的标准协议,也可以自定义协议以满足特定需求。不论选择何种方式,协议的存在都是确保通信成功的关键因素。
应用层是通信协议中最贴近程序开发者的一层,因为它直接涉及到应用程序的功能和用户体验。
应用层理很多时候都是程序猿“自定义”应用层协议的。同时也有一些现成的应用层协议。在应用层中,我们处理的主要是信息的内容和格式,而不是如何传输这些信息。这与下层的传输协议(例如TCP或UDP)形成鲜明对比,后者主要处理的是如何确保信息的可靠性和完整性。
在应用层协议的设计中,有几个关键步骤:
明确通信需求:首先,您需要知道您的应用程序要发送和接收什么类型的信息。这可能包括用户数据、文件、指令或其他任何类型的信息。
定义消息格式:一旦知道要传输的信息类型,就可以定义如何组织这些信息。这可能涉及确定哪些字段应包含在消息中,以及它们的顺序和格式。
定义交互模式:协议还需要定义通信的顺序和模式。例如,一个请求-应答模式可能要求客户端首先发送一个请求,然后服务器回应。
处理错误和异常:应用层协议还应该定义如何处理通信中的错误和异常情况,例如如果请求的数据不可用或格式错误。
现成的应用层协议包括HTTP、FTP、SMTP等,这些协议都有明确的规范描述了上述步骤。但在许多情况下,特别是对于特定领域或应用的需求,可能需要自定义协议。
自定义协议的优点是,它可以完美匹配应用的需求,提供高效的数据传输和减少不必要的开销。然而,设计一个良好的协议是一项挑战,需要深入理解应用的需求、潜在的错误条件以及如何有效地组织数据。
什么是自定义协议?我们可以通过一个例子来进一步理解它:
假设我们现在想点外卖,于是就打开了外卖软件,这个地方就涉及到了程序和服务器之间进行的网络通信交互:
请求:
用户信息+位置信息。
此处假设使用简单的格式来组织,使用文本的方式,三个属性,“,”分割。
用户信息:用户ID,用户名,手机号
位置信息:经度,纬度
请求格式示例:12345,John Doe,123-456-7890,37.7749,-122.4194
代码中构造出一个这样的字符串写到 TCP socket 和 UDP socket 中。
import socket
# 用户信息
user_id = "12345"
user_name = "John Doe"
phone_number = "123-456-7890"
# 位置信息
latitude = "37.7749"
longitude = "-122.4194"
# 构造请求字符串
request_string = f"{user_id},{user_name},{phone_number},{latitude},{longitude}"
# 服务器地址和端口
server_address = ("server_ip", server_port)
# 使用TCP套接字发送请求
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect(server_address)
tcp_socket.send(request_string.encode())
tcp_socket.close()
# 使用UDP套接字发送请求
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_socket.sendto(request_string.encode(), server_address)
udp_socket.close()
响应:
商家列表,每个商家包括名称,图片,距离,地址,简介,评分。
此处假设使用简单的格式来组织,使用文本的方式,每个商家信息占一行,每个属性使用“,”分割。
商家1:餐馆A,restaurantA.jpg,2.5 miles,123 Main St,美食餐厅,4.5
商家2:餐馆B,restaurantB.jpg,1.8 miles,456 Elm St,快餐店,4.0
商家3:餐馆C,restaurantC.jpg,3.0 miles,789 Oak St,意大利餐厅,4.2
上述过程就是自定义协议
自定义协议的具体方式是非常灵活的,自定义协议的具体格式可以根据应用的需求来设计,只要客户端和服务器都能够正确地解析和构造消息,就可以进行通信。这种自定义协议的灵活性使得开发者能够根据具体应用的需求来定义通信规则。
当然,我们刚刚说的这种通过行文本方式构造的协议属于比较粗糙的方式,实际开发时很少这么搞。
我们现在再介绍几种开发中更常见的格式:
介绍:上古时期的组织数据的格式,现在很少用于网络通信了。
Harry Potter and the Sorcerer's Stone
J.K. Rowling
19.95
The Great Gatsby
F. Scott Fitzgerald
12.99
优势:
劣势:
总之,XML是一种通用的数据格式,适用于多种应用领域,但由于其冗长的特性,对于需要高效传输和解析数据的场景,可能会选择其他格式,如JSON或Protobuf。不过,对于配置文件和数据交换等需要良好可读性的情况,XML仍然是一个有用的选择。比如maven就在使用XML来管理项目配置。
你可能觉得它和HTML有点像,其实HTML也是标签格式的数据,它属于XML的变种。
XML是一个通用的数据格式,有什么标签,含义是什么都是程序员自定义的,但是HTML则是属于一个专属的数据格式,都是由一个标准委员会规定好的:
XML(可扩展标记语言):
HTML(超文本标记语言):
标签表示段落,标签表示超链接,标签表示图像等。
XML和HTML在标签定义和使用方面有明显的区别。XML是一种通用的、自定义的标记语言,用于表示结构化数据,而HTML是专门用于构建Web页面的标记语言,其标签和含义都受标准规定。因此,XML更灵活,适用于各种数据交换需求,而HTML更专注于Web内容的呈现。
介绍:
:
分隔,键值对之间使用逗号,
分隔。{
"userID": "1000",
"position": "100,39",
"isPremiumUser": true,
"favoriteFruits": ["apple", "banana", "orange"],
"address": {
"street": "123 Main St",
"city": "Exampleville"
}
}
键固定就是String类型,很多时候就是可以把KEY的引号省略。值的话可以是 json,数组,字符串……
优势:
劣势:
总之,JSON是一种非常流行和广泛使用的数据格式,适用于多种应用场景,包括Web应用、API通信和配置文件等。它的简洁性和可读性使其成为数据交换的常见选择,除非对于性能要求极高的场景,否则JSON通常是一个有效的选项。对于更高效的二进制数据传输,可以考虑使用类似Protobuf的二进制格式。
介绍:
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
repeated string hobbies = 3;
}
优势:
劣势:
应用层中也有一些“现成”的应用层协议,其中最知名的、最广泛使用的,就是HTTP协议(超文本传输协议,用于Web通信的标准协议,负责传输超文本和多媒体资源)。
介绍:
HTTP请求:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
HTTP响应:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
Example Page
Hello, World!
特点:
应用场景:
特点:
应用场景:
注意区分,源IP和目的IP是位于网络层(IP协议)的!
在网络通信中,IP地址用于标识数据包的源和目的地。源IP表示数据包的来源,而目的IP表示数据包的目标地址。这两个地址是网络通信的基本元素,用于正确路由和传递数据包。传输层的UDP和TCP协议在IP地址的基础上提供了不同的数据传输特性,以满足不同的应用需求。
我们之前写一个服务器必须手动指定一个端口号,用端口号来区分当前主机上的不同应用程序。
写一个客户端,客户端在通信的时候也会有一个端口号,代码中虽然感受不到,但是系统会自动分配。
端口号在网络通信中是非常重要的,它用于区分不同的网络应用程序或服务,确保数据能够正确地传输到目标应用程序。
端口号范围:端口号是一个16位的整数,其范围从0到65535。通常,0不使用,所以有效的端口号范围是1到65535。
知名端口号:一些端口号被称为“知名端口号”,它们通常用于标准化的服务或协议。一些知名端口号的示例包括:
动态分配端口号:通常,大多数应用程序使用非知名端口号,也称为动态或私有端口号。这些端口号范围从1024到65535。操作系统和网络栈会自动分配这些端口号给应用程序,以确保不同应用程序之间不发生端口号冲突。
端口号的重要性:端口号的主要作用是在数据包中指定目标应用程序。当数据包到达计算机时,操作系统使用目标端口号来确定应该将数据包传递给哪个应用程序。这是实现多个网络应用程序共存的关键。
自定义端口号(普通的端口):如果你正在开发自己的应用程序或服务,可以选择使用未被占用的端口号,记住要确保你的端口号与其他应用程序的不冲突,你可以自由选择在1024到65535之间的任何端口号。
报头和载荷就可以认为是字符串拼接,当然这里是二进制的数据:
16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度;
如果校验和出错,就会直接丢弃;
1、像UDP和TCP这样的报文格式是谁规定的?
UDP(User Datagram Protocol)和TCP(Transmission Control Protocol)等网络协议的报文格式是由互联网工程任务组(Internet Engineering Task Force,IETF)规定的。IETF 是一个开放的国际组织,负责制定和管理互联网相关的标准、协议和规范。
具体来说,UDP 和 TCP 协议的报文格式以及协议的行为是由一系列标准文档和请求评论(Request for Comments,RFC)定义的。RFC 是 IETF 发布的技术文档,用于描述互联网协议、协议扩展和网络技术的规范和说明。
例如,UDP 协议的规范可以在 RFC 768 中找到,而 TCP 协议的规范则包括在 RFC 793 中。这些 RFC 文档详细描述了协议的报文格式、协议的行为、状态转换、错误处理等方面的细节。
简单来说就是,大佬们整理出了一份标准文档,称为RFC标准文档:
RFC 768: User Datagram Protocol
2、能否对UDP进行升级?比如把2个字节变成4个字节,问题就迎刃而解了。
不行!不是技术问题,而是政治问题。
要升级就得通信双方都升级,否则一边升级另一边没有,数据就无法对接,也就无法通信了。
那么全世界所有设备都得一起升级(升级操作系统内核,因为UDP是内核中实现的)。
除非有一天出现一个全新的协议把UDP替代掉……
3、可能你会觉得UDP携带的数据最大是64KB的说法不严谨?不应该是64KB-8吗?65535-8=65527?
误差不大,可以忽略不计啦~主要是方便表述。
前提:网络传输中因为一些外部干扰就可能会出现数据传输出错的情况(光/电信号,易受磁场、电场、高能离子干扰),传输低电平在干扰下变成高电平,比特翻转。所以我们就要想办法识别出出错的数据。
在网络通信中,为确保数据的正确传输,需要有可靠的校验机制。仅仅依靠单一的检测机制,如使用总数,可能是不足够的。因此,有多种校验机制被提出并使用,其中包括校验和。
校验和其实本质上也是一个字符串,体积比原始的数据更小,又是通过原始的数据生成的。原始数据相同,得到的校验和就一定相同。反之,校验和相同,原始数据大概率相同(理论上会存在不同的情况,但是实际上概率很低,可以忽略)。
校验和是一种简单而又常见的错误检测方法。它的工作原理是,通过对数据包中的每个比特位进行求和操作来生成一个特定的值—校验和值。发送方会计算这个值并附加到数据包中。而在接收端,会重新计算该数据包的校验和值,并与发送方的校验和值进行比较。如果这两个值有任何差异,这意味着在传输过程中数据可能已经被损坏。
具体操作步骤如下:
发送方计算校验和:发送方首先将数据包中的每个比特位相加,得到一个校验和值。这个校验和值通常使用二进制补码加法进行计算。通过对数据包的所有比特位进行求和,发送方生成了一个表示数据包内容的唯一校验和值。
发送方附加校验和值:发送方将计算得到的校验和值添加到数据包的头部或尾部。这样,接收方在接收数据包时可以轻松地找到并提取校验和值。
数据包传输:带有附加校验和值的数据包被发送到接收方。这个数据包包含原始数据以及校验和值。
接收方计算校验和:接收方接收到数据包后,也会对数据包中的每个比特位进行相加操作,使用相同的校验和算法来计算一个新的校验和值。这个计算过程是在接收方的硬件或软件中完成的。
比较校验和值:接收方得到新的校验和值后,将其与数据包中附加的校验和值进行比较。如果两个校验和值匹配,说明数据包在传输过程中没有发生错误,接收方可以安全地使用数据。
错误检测:如果接收方计算得到的校验和值与数据包中的校验和值不匹配,这意味着数据包在传输过程中发生了错误。接收方可以选择丢弃该数据包以避免错误数据的使用,或者请求发送方重新发送数据包。如果校验和值相同,那么大概率就是相同的。
校验和的优点是它可以有效地检测到传输错误,尤其是在网络传输中可能发生的比特翻转等问题。但其缺点是不能修正错误,仅能进行错误检测。因此,当校验和不匹配时,通常需要其他高级的机制来修复错误或重新传输数据。
校验和的生成过程在一些情况下可能会显得低效和繁琐,特别是对于大型数据包的情况。
校验和的生成过程涉及对数据包中的每个比特位进行求和操作,这可能需要大量的计算,尤其是在数据包很大的情况下。
一种优化校验和生成过程的方法是使用硬件加速或专用的校验和生成器。这些硬件模块能够高效地执行校验和计算,从而提高性能。此外,现代处理器也支持一些指令集扩展,如SSE(Streaming SIMD Extensions)和AVX(Advanced Vector Extensions),这些扩展可以用于加速校验和计算。
另一种方法是使用校验和算法的实现库,这些库通常会提供高度优化的校验和计算函数,可以显著提高性能并减少繁琐的手动计算工作。例如,许多编程语言和操作系统都提供了内置的校验和计算函数,可以轻松地在应用程序中使用。
在网络中,尤其是在使用UDP协议时,校验和是非常重要的一个机制。UDP是一个无连接、不可靠的传输协议,因此需要额外的检测方法来确保数据的正确传输。UDP的校验和正是为此设计的,用于检测数据包在传输过程中是否被损坏。
计算校验和有很多种算法,UDP中使用的是CRC算法:
循环冗余检验(Cyclic Redundancy Check,CRC)是一种常用于检测数据传输中错误的校验方法。它通过在数据中添加一组冗余数据(通常称为校验码或CRC码)来检测数据是否在传输过程中发生了错误或损坏。CRC通常用于数据通信中,特别是在计算机网络、存储系统和无线通信中。
CRC的主要原理是在发送数据之前,计算出一个CRC码,并将其附加到数据中。接收方在接收数据后,也会计算出一个CRC码,并将其与接收到的CRC码进行比较。如果两个CRC码不匹配,就表示数据在传输过程中可能发生了错误,接收方可以选择丢弃数据或要求重新传输。
CRC的特点和原理包括:
多项式运算:CRC使用多项式运算来生成和检测CRC码。发送方根据数据内容和特定的多项式生成CRC码,附加到数据中。接收方使用相同的多项式来计算接收到的数据的CRC码,然后与接收到的CRC码进行比较。
固定位数的校验码:CRC生成的校验码通常是固定位数的,例如16位或32位。这意味着CRC码的长度不随数据长度而变化,因此它相对简单且高效。
高错误检测能力:CRC能够检测多种错误类型,包括单比特翻转、多比特错误和某些种类的传输错误。它在检测错误方面具有较高的可靠性。
不纠正错误:与一些其他校验方法不同,CRC主要用于检测错误而不是纠正错误。如果CRC检测到错误,通常是丢弃数据或请求重新传输,而不是尝试修复数据。
总的来说,CRC是一种广泛应用于数据通信中的校验方法,它能够帮助确保数据的完整性,尤其适用于那些对数据准确性要求较高的应用场景。不过需要注意的是,CRC并不能保证绝对的安全性,CRC不是特别靠谱,导致两个不同的数据得到相同的CRC校验和的概率较大:比如前一个字节恰好少1,后一个字节恰好多1,相互抵消。因此在某些敏感性要求极高的场合,可能需要其他更加复杂的错误检测和纠正方法。
MD5(Message Digest Algorithm 5):
MD5(Message Digest Algorithm 5)是一种常用的哈希函数,用于生成固定长度的128位哈希值。
有一系列公式来完成计算,仅作了解:
MD5的计算过程涉及一系列的操作和公式,以下是MD5算法的主要步骤和公式:
填充数据:将输入数据按照一定规则填充到一个长度为64字节(512位)的消息块中。这个填充过程确保了消息块的长度是64字节的整数倍。
初始化缓冲区:MD5算法使用四个32位的缓冲区(A、B、C、D)来存储中间结果。这些缓冲区的初始值是固定的。
分割消息块:将填充后的消息块分成16个32位的子块。
循环处理:MD5算法将消息块分为64个操作步骤,分为4轮(每轮16步)。每一步都使用一种不同的运算函数,并更新缓冲区中的值。
四轮操作:
- 第一轮:每一步使用位运算函数F、G、H、I和数据子块中的数据,对缓冲区中的值进行更新。
- 第二轮:每一步使用不同的运算函数和数据子块中的数据。
- 第三轮:同样使用不同的运算函数和数据子块中的数据。
- 第四轮:最后一轮的运算函数和数据子块。
计算最终哈希值:将四个32位缓冲区中的值按一定顺序连接起来,形成最终的128位MD5哈希值。
特点:
定长输出:MD5生成的哈希值始终是128位(16字节)长,无论输入数据的长度如何(校验和本身就不应该太长,不然不方便网络传输)。
分散性:MD5被设计为对输入数据的微小变化高度敏感。给定两个原始数据,哪怕大部分内容都相同,只要输入数据中有一个字节发生变化,生成的MD5哈希值也会发生明显变化。所以它也非常适合作为hash算法。
不可逆性:MD5是一种不可逆哈希函数,意味着从MD5哈希值无法还原出原始输入数据,因为计算量非常大,以至于超出了现有的计算机的算力极限,理论上不可行。这种特性对于密码学中的存储密码哈希值非常有用,因为攻击者无法轻易还原用户密码。
速度:MD5的计算速度相对较快,适用于大多数常规用途。但它已经被发现存在一些弱点,不再被认为足够安全,因此在安全敏感的场景中不建议使用。
SHA-1(Secure Hash Algorithm 1):
特点:
定长输出:SHA-1生成的哈希值是160位(20字节)长,也具有定长输出的特性。
分散性:与MD5类似,SHA-1也对输入数据的微小变化非常敏感,即使少量字节发生变化,SHA-1哈希值也会显著不同。
不可逆性:SHA-1同样是一种不可逆哈希函数,难以从哈希值还原出原始数据。
安全性:尽管SHA-1在过去广泛使用,但随着时间的推移,它的安全性逐渐受到威胁。现在已经发现了一些攻击方法,使得SHA-1哈希值的碰撞(不同输入产生相同的哈希值)更容易发生,因此在安全敏感的应用中,SHA-1已不再推荐使用。
MD5(Message Digest Algorithm 5)和SHA-1(Secure Hash Algorithm 1)是常见的哈希算法,用于生成数据的哈希值。它们在数据完整性校验、数据摘要、密码学和数字签名等领域都有广泛应用。
基于UDP的应用层协议包括许多不同的协议,这些协议用于实现各种网络应用。以下是常见的一些基于UDP的应用层协议(虽然但是……比起TCP,用UDP的真的不多了):
NFS(Network File System):NFS是一种分布式文件系统协议,用于在网络上共享文件和资源。它允许远程计算机像访问本地文件一样访问远程文件。
TFTP(Trivial File Transfer Protocol):TFTP是一个简单的文件传输协议,通常用于无需高级功能的文件传输,例如,用于启动和配置网络设备。
DHCP(Dynamic Host Configuration Protocol):DHCP是一种用于自动分配IP地址和其他网络配置信息的协议。它允许计算机自动获取IP地址,以便它们可以连接到网络。
BOOTP(Bootstrap Protocol):BOOTP是DHCP的前身,它用于引导计算机并获取其网络配置信息。它通常用于引导时期,然后计算机切换到DHCP以获取持久性配置。
DNS(Domain Name System):DNS是用于将域名解析为IP地址的协议,以便计算机可以识别和连接到特定的网络资源。
你自定义的UDP应用层协议:开发人员可以根据其特定需求创建自定义的基于UDP的应用层协议。这些协议可以用于各种用途,如游戏通信、传感器数据传输等。
TCP,即Transmission Control Protocol,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。
TCP这个协议最大的特点,也是它的初心,就是“可靠传输”。
TCP协议格式:
TCP(传输控制协议)是一种面向连接的协议,用于在计算机网络中可靠地传输数据。以下是TCP的一些重要特性:
可靠性:TCP提供可靠的数据传输,确保数据在发送和接收之间的可靠传输。它使用确认机制来确认数据的接收,如果数据包丢失或损坏,TCP会重新发送丢失的数据,直到接收方确认。
有序性:TCP保持发送的数据包的顺序与接收方接收的顺序一致。这意味着即使数据包在传输过程中出现乱序,TCP也会将它们重新排序以确保按正确顺序传递给应用程序。
流控制:TCP使用滑动窗口机制来控制发送方的发送速率,以避免发送方超出接收方的处理能力。这有助于防止数据包丢失和网络拥塞。
拥塞控制:TCP具有拥塞控制机制,可检测并响应网络拥塞。它通过减小发送速率来减轻拥塞,以避免网络崩溃。
面向连接:TCP是一种面向连接的协议,这意味着在数据传输之前需要在发送方和接收方之间建立连接。连接建立包括三次握手(三次交换确认),以确保双方都准备好进行通信。
全双工通信:TCP支持全双工通信,允许双方同时发送和接收数据。这使得双向通信成为可能,例如在Web浏览器和Web服务器之间的通信中。
面向字节流:TCP是面向字节流的协议,而不是面向消息的。这意味着它将数据视为一连串的字节,而不考虑消息的边界。应用程序必须自己管理消息的分割和重组。
可靠性和性能平衡:TCP在可靠性和性能之间进行权衡。它使用超时和重传机制来确保可靠性,但这可能会在一些情况下引入延迟。为了提高性能,TCP还使用窗口机制来允许多个数据包在一个确认之前发送,从而提高了吞吐量。
面向网络层协议:TCP位于传输层,并依赖于底层的IP协议。它使用IP地址来寻址和路由数据,以便将数据传递到正确的目标。
总之,TCP是一种可靠、有序、面向连接的协议,适用于需要确保数据完整性和可靠性的应用程序,如文件传输、电子邮件、Web浏览等。然而,由于它引入了一些开销,对于对实时性要求较高的应用程序可能不太适合,因此有时会选择使用UDP等其他协议。
我们刚刚说过,可靠传输是TCP最最最核心的特性!所以来着重讲一讲:
可靠传输不是说发送方把数据能够100%的传输给接收方,这样要求太高了。
所以我们退而求其次,至少得做到:
1、发送方发出数据之后,能够知道接收方是否收到数据了。
2、一旦发现对方没收到,就可以通过一系列手段补救。
那么,TCP的可靠传输是怎么达成的呢?
这就涉及到一个非常重要的概念——“确认应答”。
TCP的核心特性之一就是其可靠性。它确保数据从发送方传输到接收方,即使在网络状况不佳的情况下也是如此。这是通过一系列机制来实现的,其中最重要的机制是确认应答(ACK)。
“确认应答”是TCP实现可靠传输的核心机制之一。通过确认应答,TCP确保发送方可以得知接收方是否成功接收了发送的数据,以及哪些数据已经被接收。发送方把数据发给接收方之后,接收方收到数据就会给发送方返回一个应答报文(acknowledge,ack),发送方如果收到这个应答报文,就知道自己的数据是否发送成功了。
但是应答报文有一个“致命的缺陷”!
如果发送方先发了第一个数据过去,在接收方还未应答的时候就又发了第二个数据。而接收方会给出两个应答。但是,实际上网络传输数据很有可能会出现“后发先至”这种情况,也就是说给第二个数据的应答发给了第一个……
后发先至,也是一种 乱序问题:
在网络中,数据包可能会按不同的路径到达目的地,这可能导致数据包的到达顺序与它们被发送的顺序不一致。这就是乱序问题,即后发送的数据包在接收端可能会比先发送的数据包更早到达。一个数据包在进行传输的过程中走的路径可能是非常复杂的,不同的数据报可能走不同的路线。
出现这种情况,TCP需要完成两个工作:
确保应答报文和发出去的数据能对上号,不要出现歧义。
确保再出现后发先至的现象时,能够让应用程序仍然按照正确顺序来理解数据。
序号就是一个整数,大小关系描述了数据的先后顺序。
序号不是按照一条两条的方式来编号的,而是按照字节来编号的。毕竟TCP是面向字节流的。
当然,上图只是假设最开始传输的时候第一个字节序号是1,但是TCP传输的时候初始序号一般不是从1开始的。
一个TPC数据报理一共有10001个字节的载荷数据,其中第一个字节的序号是1,就在TCP报头的序号字段中写1。由于一共是1000个字节,此时最后一个字节的序号自然就是1000.但是1000这样的数据并没有在TCP的报头中记录,因为TCP包头中记录的序号是这一次传输的载荷数据中的第一个字节的序号,剩下的其他字节的序号都需要依次的推出。在应答报文中就会在确认序号字段中填写1001,因为收到的数据是1-1000,所以1001之前的数据都被B收到了,也可以理解成B接下来要向A索要1001开始的数据。
通过特殊的ACK数据包里面携带的“确认序号”告诉发送方哪些数据已经被确认收到了,此时发送方就大概知道自己刚刚发送的数据是被接收了还是没有。
流水线传输:TCP使用序列号和确认号来支持流水线传输。发送方可以发送多个数据段,而无需等待确认应答,因为它可以根据确认号了解哪些数据已经被接收,哪些数据可以继续发送。这提高了数据传输的效率。
那么如何区分一个数据包是普通的数据还是ACK应答数据呢?
所以,达成可靠传输的最核心的机制就是“确认应答”。你一定要牢记这点,因为我们之后还会遇到其他一些辅助机制。
虽然说“确认应答”是确保可靠传输的核心机制,但是只有ACK机制还不够,我们还需要其他一些手段来实现真正的可靠性,先简单来看一下:
确认应答(ACK): 如您所说,当接收方收到数据时,它会向发送方发送一个ACK报文,告知发送方哪些数据已被接收。每个数据段都有一个唯一的序列号,使得发送方知道哪些数据被确认。
超时和重传: 如果发送方在一段时间内没有收到接收方的ACK报文,它会假设该数据段已丢失,并重新发送数据。这个时间间隔被称为重传超时(Retransmission Timeout, RTO)。
滑动窗口: TCP使用滑动窗口机制进行流量控制,确保发送方不会溢出接收方的缓冲区。发送窗口定义了发送方可以发送而不等待ACK的数据量。接收窗口定义了接收方可以接收的数据量。
序列号和确认号: TCP报文段头部有两个重要的字段:序列号和确认号。序列号用于对数据字节进行编号,而确认号表示期望接收的下一个字节的序列号。
快速重传: 当接收方收到一个失序的报文段时,它会立即重复发送当前的ACK报文(不等待通常的确认时间)。当发送方连续收到三个重复的ACK时,它会立即重新发送未确认的报文段,而不等待RTO超时。
错误检测: TCP报文段包括一个校验和字段,用于检测传输错误。如果接收方检测到报文段中的错误,它将丢弃该报文段,该报文段不会被确认。这导致发送方超时并重新发送该报文段。
三次握手: 在建立连接之前,TCP使用三次握手机制来确保双方都准备好通信并确认双方的初始序列号。
四次挥手: 当通信结束时,TCP使用四次挥手机制来终止连接,确保所有数据都被正确地传输。
所有这些机制共同工作,确保TCP的可靠传输。虽然TCP无法保证100%的数据传输成功,但它确保了即使在丢包、延迟和其他网络问题的情况下,数据也可以被可靠地传输。
当然,你一定要明确:确认应答才是核心,其他机制均为辅助!
确认应答表述的是一个比较理想的情况,但是如果网络传输过程中丢包了,发送方势必无法收到ACK,咋办???
此时,我们就使用超时重传机制,针对确认应答,进行补充。
为什么会发生丢包?
在网络中发生数据包丢失的情况可能有多种原因,其中一些常见的原因包括:
网络拥塞:当网络流量过大或网络设备过载时,数据包可能会在路由器或交换机上丢失。这是因为网络设备的缓冲区已满,无法继续接受新的数据包。网络中,数据包太多,就会在这些路由器/交换机上出现“堵车”,但是路由器针对“堵车”的处理往往是比较粗暴的,不会把积压的数据包都保存好,而是会把其中的大部分数据包都直接丢弃掉,此时这个数据包就在网络上消失了。
链路问题:物理链路上的问题,如电缆故障、网络设备故障或信号干扰,都可以导致数据包丢失。
路由问题:错误的路由配置或网络拓扑变化可能导致数据包被发送到错误的目标或被丢弃。
临时性网络故障:有时网络中的临时性故障,如闪电击中网络设备或临时性网络连接中断,可能导致数据包丢失。
包裹太大:如果数据包超过网络的最大传输单元(MTU),它可能会被分割成更小的片段。在某些情况下,这些片段可能会在传输过程中丢失。
网络环境错综复杂,丢包是一项随机事件。在上述TCP的传输过程中,丢包存在两种情况:
1、数据包直接丢失:
主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B;
如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发。
那这种情况下,数据丢了返回的应答报文是发送的数据的第一个字节吗?
如果你的数据丢了是不会触发ACK的,对端都没有收到数据,自然不会返回ACK。
后续在滑动窗口机制下可能会返回ACK是发送数据的第一个字节。
2、但是,主机A未收到B发来的确认应答,也可能是因为ACK丢失了:
站在发送方的角度,它无法区分这两种情况,所以发送方能做得到的补充措施就是“重新传输”。
由于以上原因,TCP必须具备一种机制来处理丢失的数据包。这就是超时重传机制的作用:
超时重传机制:当发送方发送数据后,它会启动一个定时器,等待一定的时间来接收来自接收方的确认应答(ACK)。如果在定时器超时之前未收到ACK,发送方会假设数据包已经丢失,并重新发送它。这就是超时重传机制的基本工作原理。
RTO(Retransmission Timeout):超时时间是根据网络条件和拥塞情况动态计算的,通常称为RTO。发送方会根据RTO的值来设置定时器。如果在RTO内未收到ACK,就会触发数据包的重传。
两次都丢包的概率会比一次低很多,所以重传操作大幅度提升了数据传输成功的概率,是一个很好的丢包的补救措施。
当我们引入可靠性的时候是会付出代价:传输效率变低,复杂程度变高。
这也是我们为什么说:TCP对于对实时性要求较高的应用程序可能不太适合,因此有时会选择使用UDP等其他协议,这也是UDP能够存在下来而不被TCP完全取代的一样所在。
回到刚刚 “主机A未收到B发来的确认应答,也可能是因为ACK丢失了”那里:
丢失的确认应答:
当主机A发送数据后,如果它未收到主机B发来的确认应答(ACK),这并不一定意味着传输的数据包丢失了。有可能是应答报文(ACK)在传输过程中丢失了。
重复的数据包:
因为主机A未收到应答,它可能会重新发送数据。这意味着主机B可能会收到多个重复的数据包。
识别和处理重复的数据包:
为了处理这种情况,TCP协议具有识别和丢弃重复数据包的能力。这是通过TCP报文头中的序列号来实现的。接收端可以检查收到的数据包的序列号,并与预期的序列号进行比较,以识别和丢弃任何重复的数据包。
TCP会有一个“接收缓冲区”,就是一个内存空间,会保存当前已经收到的数据以及数据的序号。接收方如果发现当前发送过来的数据是以及在接收缓冲区中存在的(收到过重复的数据),接收方就会直接把这个后来的数据直接丢弃掉,确保应用程序进行read的时候读到的只有一条数据。
接收缓冲区不仅仅能进行去重,还能进行重新排序,确保发送到顺序和应用程序读取到的顺序是一致的。
确定超时时间:
超时时间是发送方等待应答的时间。理想情况下,这应该是一个尽可能短的时间,确保在此时间内能收到确认应答。但是这个时间的长短,随着网络环境的不同是有差异的 ,可能需要动态调整。
那么,如果超时的时间如何确定?
超时时间的选择:
如果超时时间设的太长,会影响整体的重传效率; 如果超时时间设的太短,有可能会频繁发送重复的包。
动态超时时间的计算:
TCP动态计算超时时间,确保在不同的网络条件下都能高效地传输数据。
超时时间的动态计算是TCP中的一项非常重要的机制,被称为RTO(Retransmission Timeout)的计算。RTO的合理设置对于TCP的性能和可靠性至关重要。以下是有关RTO的更多详细信息:
RTO的计算是根据网络条件和历史数据包传输的情况进行动态调整的。通常,RTO的计算包括以下步骤:
初始化RTO:在连接建立时,TCP通常会初始化RTO为一个较小的默认值,例如200ms。这个初始值可以根据特定的TCP实现和网络配置而有所不同。
RTT(Round-Trip Time)的测量:TCP通过测量往返时间来估计数据包从发送到接收的时间。每当发送方发送一个数据包并等待其ACK应答时,它会测量该数据包的RTT。
平均RTT的计算:TCP维护一个平均往返时间的估计值,通常使用加权移动平均(Weighted Moving Average)来计算。这个平均RTT值用于确定RTO。
偏差计算:除了平均RTT,TCP还计算了RTT的偏差,即RTTVAR(RTT Variance)。偏差表示RTT的变化范围。它用于确定RTO的“安全余量”。
RTO计算:TCP使用以下公式来计算RTO:
RTO = 平均RTT + 4 * RTTVAR
这个公式中的4是一个常数,通常用于提供足够的安全余量以应对网络不稳定性和变化。
超时重传的指数退避:如果在RTO内未收到ACK,发送方将启动重传,并且在每次超时后会将RTO加倍。这是为了适应不同网络条件下的动态变化,以确保超时时间不会过于频繁或过于长。简单来说就是等待时间会动态变化,每多经历一次超市,等待时间都会变长。
最大RTO限制:TCP通常还会设置一个最大的RTO值,以防止RTO变得过于巨大,从而导致不必要的等待。一般情况下,最大RTO值在几秒钟到几分钟之间。毕竟等待时间不可能无限拉长,得设置TCP的重置连接。
总之,RTO的计算是TCP中的关键部分,它确保了在不同网络条件下的可靠数据传输。通过动态计算RTO,TCP可以适应网络的变化,并确保重传机制的效率和可靠性。这使得TCP能够在各种网络环境中提供高性能的通信。
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。
超时时间的实现:
在Linux(以及BSD Unix和Windows)中,超时时间通常以500ms为单位进行控制。每次超时重传的判定时间都是500ms的整数倍。
指数退避策略:
当发送方重发数据后仍然没有收到应答,它会等待2倍的当前超时时间再进行重传。这意味着,第一次重传等待500ms,第二次等待1000ms,第三次等待2000ms,以此类推。这称为指数退避策略。
强制关闭连接:
如果连续重传多次仍然未收到应答,TCP最终会认为网络或对端主机出现了问题。经过一定数量的重传尝试后,TCP会放弃并强制关闭连接。
通过这一系列机制,TCP确保了在面对网络不稳定性和数据包丢失时仍然可以可靠地传输数据。
可能你还是有点懵?那让我再进一步向你解释一下这个策略的工作方式:
超时计算:初始超时时间通常设置为一个相对较短的值,例如500ms。这个超时时间是基准超时时间。
第一次超时:如果发送方在基准超时时间内未收到应答(ACK),它会触发第一次超时重传。然后,它会等待2倍的基准超时时间,即等待1秒(2 * 500ms)。
第二次超时:如果第一次重传后仍未收到应答,发送方会触发第二次超时重传。这次超时等待4倍的基准超时时间,即等待2秒(4 * 500ms)。
依次类推:如果第二次超时重传后仍未收到应答,发送方会依次继续增加等待时间,以指数形式递增。每次等待时间都是前一次等待时间的两倍。
最大超时时间限制:通常,TCP会设置一个最大超时时间的限制,以防止等待时间过长。一旦超时时间超过这个限制,TCP可能会放弃等待并认为网络或对端主机出现了异常情况。
通过这种指数退避策略,TCP可以在不浪费太多时间的情况下等待应答,并在必要时进行重传。这使得TCP能够适应不同网络条件下的变化,同时保持高效的数据传输。最终,如果连续的重传次数达到一定阈值,TCP会强制关闭连接,以避免不必要的等待和资源浪费。这有助于确保网络连接的稳定性和可靠性。
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接。
“握手”就是建立连接,“挥手”就是“断开连接”。
TCP的握手就是给对方传输一个简短的没有业务数据的数据包,通过这个数据包来唤起对方的注意,从而触发后续的操作。
三次握手(Establishing a Connection):
客户端向服务器发起连接请求:客户端发送一个SYN(同步)报文段,它是一个特殊的TCP数据包,没有载荷(不携带业务数据,即不携带应用层数据包。其中包含一个随机的初始序列号,表明客户端希望建立连接。客户端进入SYN_SENT状态。
服务器确认连接请求:服务器收到客户端的SYN报文段后,如果同意建立连接,会发送一个SYN-ACK报文段,其中包含一个随机的初始序列号,以及确认客户端的序列号。服务器进入SYN-RCVD状态。
客户端确认服务器的响应:客户端收到服务器的SYN-ACK报文段后,会发送一个ACK(确认)报文段,确认服务器的响应。此时,连接建立成功,客户端和服务器都进入ESTABLISHED状态,可以开始进行数据传输。
四次挥手(Terminating a Connection):
客户端请求关闭连接:当客户端决定关闭连接时,它发送一个FIN(结束)报文段,表示不再发送数据,但仍愿意接收数据。客户端进入FIN_WAIT_1状态。
服务器确认关闭请求:服务器收到客户端的FIN报文段后,会发送一个ACK报文段来确认关闭请求。此时,服务器进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。
服务器完成数据传输后关闭连接:服务器在完成数据传输后,也决定关闭连接,发送一个FIN报文段给客户端。服务器进入LAST_ACK状态。
客户端确认关闭请求:客户端收到服务器的FIN报文段后,发送一个ACK报文段来确认关闭请求。此时,客户端和服务器都进入CLOSED状态,连接彻底关闭。
通过这个三次握手和四次挥手的过程,TCP确保了连接的建立和断开都是可靠的。这些步骤确保了数据的可靠传输,并防止了重复或不必要的连接建立。这是TCP协议在互联网上可靠通信的基础。
此时“握手”完成,记录了对方的信息,也就是构成了“逻辑”上的连接。
建立连接的过程其实是通信双方都要给对方发起SYN,也都要给对方返回ACK,一共四次握手,中间两次可以合并成一次,变成三次握手。
和三次握手不同,四次挥手不能把中间的两次交互合二为一,因为四次挥手的ACK和第二个FIN的触发时机是不同的。ACK是内核响应的,B收到FIN,就会立即返回ACK。第二个FIN是应用程序的代码触发,B这边调用了close方法才会触发FIN。从服务器收到FIN(同时返回ACK),再到执行到close发起FIN,这中间要经历多少时间都是不确定的,和你写的代码有关。FIN就会在socket对象close的时候被发起,可能是手动调用close方法,也可能是进程结束。
这是否意味着这边代码close没写/没执行到,是不是第二个FIN一直发不出去?有可能!
正常的四次挥手是正常的流程断开的连接;
不正常的回收,没有挥完四次,异常的流程断开连接,也是存在的:
结果:这些情况可能导致四次挥手的流程无法正常完成,连接可能保持在CLOSE_WAIT、FIN_WAIT等状态,或者进入TIME_WAIT状态并占用资源,直到超时。
我们马上要讲的一个机制——延迟应答,它能够拖延ACK的回应时间,一旦ACK滞后,就有机会和下一个FIN合并在一起了。
延迟应答机制(Delayed ACK)是一个重要的概念,它可以影响四次挥手的流程,特别是在正常的四次挥手中,ACK的延迟可以影响FIN的合并。
延迟应答机制的核心思想是,在某些情况下,TCP栈会将ACK的发送延迟一段时间,以尝试合并多个ACK。这可以减少网络上的控制包(如ACK)数量,从而提高效率。
对于四次挥手来说,这意味着在接收到对方的第一个FIN后,TCP栈可能会等待一段时间,看是否有数据要发送,如果在这段时间内没有数据要发送,它会合并ACK和自己的FIN,将它们一起发送。这有助于减少不必要的包传输,但也可能导致延迟。
需要注意的是,延迟应答机制的行为取决于TCP栈的具体实现和配置。某些情况下,可能需要手动配置或调整延迟应答的参数,以确保其在特定应用场景中表现良好。
在TCP连接的过程中,服务器端的状态和状态转换如下:
LISTEN(监听状态):当服务器应用程序调用 listen 函数,服务器的套接字进入LISTEN状态。在LISTEN状态下,服务器准备好接受传入的连接请求,等待客户端建立连接。服务器可以同时监听多个端口,每个监听的端口都会对应一个LISTEN状态的套接字。
ESTABLISHED(已建立连接):当客户端发起连接请求,服务器接受请求后,服务器套接字进入ESTABLISHED状态。这表示连接已经成功建立,服务器和客户端可以开始进行数据的传输和通信。
CLOSED(断开连接):连接彻底断开,可以释放了。
服务器端的状态转换通常涉及到以下情况:
SYN_RCVD(同步接收状态):当服务器接收到客户端的SYN(同步)包,服务器进入SYN_RCVD状态,表示它已经收到了客户端的连接请求。
ESTABLISHED(已建立连接):一旦服务器确认了客户端的连接请求,它将进入ESTABLISHED状态,允许双方进行数据传输。
CLOSE_WAIT(等待关闭状态):当服务器希望关闭连接时,它会发送一个FIN(结束)包给客户端,进入CLOSE_WAIT状态。在这个状态下,服务器等待客户端的确认或响应。
CLOSING(关闭中状态):CLOSING状态表示连接即将关闭,但由于某些原因,还没有完全完成。
LAST_ACK(最后确认状态):当服务器发送一个FIN包后,等待客户端的确认,服务器进入LAST_ACK状态。在这个状态下,服务器等待客户端的确认,以确认双方都已经完成数据的传输。
TIME_WAIT(等待时间状态):哪一边主动断开连接,哪一边就会进入TIME_WAIT状态。当服务器发送最后的ACK包后,它进入TIME_WAIT状态,以确保它的最后一个ACK已经被客户端接收。在TIME_WAIT状态下,服务器会等待一段时间后才彻底关闭连接。这个状态主要存在的意义就是为了防止最后一个ACK丢失留下的后手。毕竟如果最后一个ACK丢失,站在B的角度,它就会触发超时重传,重新把刚才的FIN再传一遍。如果A没有TIME_WAIT状态,就意味着A这个时候已经完全真正释放连接了,此时重传的FIN也就没有人能够处理,没有人可以返回ACK了。B就永远收不到ACK了。A如果使用TIME_WAIT状态进行等待,等待的时间就是为了处理B重传的FIN,此时重传的FIN来了,就可以继续返回ACK,B重传FIN才有意义。那么TIME_WAIT等待多久呢?假设网络上两个节点通信消耗的最大时间是MSL,此时的时间就是2MSL(上限)。MSL是一个可配置的参,绝大部分的时间都没有2MSL那么长。
CLOSED(断开连接):连接彻底断开,可以释放了。
这些状态转换描述了服务器端在进行TCP连接的不同阶段的状态变化。注意,实际的状态转换可能会因操作系统和网络协议栈的实现而有所不同。
通常情况下,建立TCP连接是由客户端主动发起的,而断开连接可以由客户端或服务器双方任何一方主动发起。这是因为TCP协议是全双工的,允许双方在通信过程中独立地发起连接和断开连接。这种灵活性使得双方都能够有效地管理连接的建立和断开,以满足应用程序的需求。在许多情况下,客户端通常在完成数据传输后主动发起断开连接,而服务器则接受并响应这一断开请求。
CLOSED -> LISTEN:服务器初始状态是CLOSED。当服务器调用 listen 后,它进入LISTEN状态,准备接受客户端连接请求。
LISTEN -> SYN_RCVD:当服务器在LISTEN状态下监听到一个连接请求(SYN报文段),它将连接放入内核等待队列,并向客户端发送SYN-ACK报文段。此时服务器进入SYN_RCVD状态。
SYN_RCVD -> ESTABLISHED:如果客户端收到服务器的SYN-ACK并响应ACK,连接建立成功,服务器进入ESTABLISHED状态,可以开始进行数据传输。
ESTABLISHED -> CLOSE_WAIT:当客户端主动关闭连接(调用 close),服务器会收到结束报文段(FIN),服务器返回确认报文段,并进入CLOSE_WAIT状态,表示它已经接受了关闭请求。
CLOSE_WAIT -> LAST_ACK:在CLOSE_WAIT状态下,服务器可以继续处理未完成的数据传输。当服务器最终准备关闭连接时,它向客户端发送FIN报文段,进入LAST_ACK状态,等待最后一个ACK的确认。
LAST_ACK -> CLOSED:服务器收到了来自客户端的对FIN的ACK确认,连接被完全关闭,服务器进入CLOSED状态。
CLOSED -> SYN_SENT:客户端初始状态是CLOSED。当客户端调用 connect 时,它发送一个SYN报文段,尝试建立连接,并进入SYN_SENT状态。
SYN_SENT -> ESTABLISHED:如果客户端的SYN报文段被服务器接受,并且服务器响应了SYN-ACK,连接建立成功,客户端进入ESTABLISHED状态,可以开始进行数据传输。
ESTABLISHED -> FIN_WAIT_1:当客户端主动调用close
关闭连接时,它发送一个FIN报文段,表示要关闭连接,并进入FIN_WAIT_1状态,等待服务器的确认。
FIN_WAIT_1 -> FIN_WAIT_2:客户端在FIN_WAIT_1状态下等待服务器的确认。一旦收到服务器的确认,客户端进入FIN_WAIT_2状态,继续等待服务器的结束报文段。
FIN_WAIT_2 -> TIME_WAIT:客户端在FIN_WAIT_2状态下等待服务器发送的结束报文段(FIN)。一旦收到服务器的FIN,客户端进入TIME_WAIT状态,等待一段时间(2MSL)以确保所有报文段都已被处理。
TIME_WAIT -> CLOSED:在TIME_WAIT状态下,客户端等待一段时间(2MSL)后,最终进入CLOSED状态,表示连接已完全关闭。
下图是TCP状态转换的一个汇总:
较粗的虚线表示服务端的状态变化情况;
较粗的实线表示客户端的状态变化情况;
CLOSED是一个假想的起始点,不是真实状态。
"半关闭"(Half-Closed)是指在TCP连接中的一种状态,其中一端(通常是其中一个端点)关闭了其数据发送通道,但仍然可以接收数据。这种状态通常出现在一个端点希望终止数据发送,但仍然需要接收来自对端的数据的情况下。
举个男女朋友分手的例子来解释半关闭状态:
假设男女朋友之间建立了一种类似于TCP连接的关系,他们之间在通信中可能存在以下状态:
ESTABLISHED:男女朋友之间正常通信,互相发送和接收消息。
FIN_WAIT_1:其中一方(例如男方)决定要结束关系,发送了一个“分手”的消息(类似于TCP的FIN报文),但仍然愿意听取对方的回应。
FIN_WAIT_2:男方在FIN_WAIT_1状态下等待对方(女方)的回应,但仍然可以接收她的消息。
CLOSE_WAIT:女方在接收到男方的“分手”消息后,也决定结束关系,并发送了自己的“分手”消息。此时,女方的状态为CLOSE_WAIT,表示她已关闭发送通道,但仍然愿意接收男方的消息。
LAST_ACK:男方在接收到女方的“分手”消息后,进入LAST_ACK状态,表示他已经关闭了发送通道,但仍然愿意接收女方的最后一条消息,以确认她已收到“分手”消息。
TIME_WAIT:在男方发送最后一条消息后,他进入TIME_WAIT状态,等待一段时间(2MSL,最大报文生存时间),以确保所有可能的报文都已经被处理,然后他会彻底关闭连接。
总结,半关闭状态允许一方关闭其数据发送通道,但仍然能够接收来自对方的数据,以便处理一些收尾工作或确认对方已收到关闭请求。这有助于确保数据传输的完整性和可靠性。
至于CLOSING状态,它是在关闭过程中的一个临时状态,表示等待最后一个ACK的确认,通常不会长时间停留在这个状态。
TIME_WAIT状态的存在是为了解决TCP连接的最后阶段的一些问题,如确保最后的ACK能够可靠到达,以及处理潜在的重传数据包。2MSL的时间是一个安全的等待期,用于确保所有可能的报文段都已经被处理。这有助于防止旧的报文段在新的连接上引起混淆。
CLOSE_WAIT状态在服务器端可能出现,通常是因为服务器没有正确地关闭socket,导致四次挥手未正确完成。这可能是一个BUG,应该通过正确地关闭socket来解决。关闭socket时,确保在合适的时间内调用close,以便正确完成四次挥手过程,而不会在服务器上留下大量的CLOSE_WAIT状态。
我现在想问:三次握手到底是要解决什么问题?通过四次握手是否可行?两次握手呢?
TCP是为了可靠传输,能够进行确认应答和超时重传有个大前提就是当前的网络环境是基本可用的通畅的。如果当前网络已经存在重大故障了,此时可靠传输无从谈起。
所以三次握手核心作用:
确认网络通畅:三次握手通过来回的握手和确认过程,确保了双方能够正常通信。这是“投石问路”的作用,建立网络连接,验证了网络的可用性。
确认双方的发送和接收能力:每一步的握手都需要对方的确认,这意味着双方不仅能够发送数据,还能够正常接收、处理和响应数据。这有助于确保双方的通信能力正常。
针对重要参数进行协商,建立初始序列号:通信双方在握手过程中会需要针对重要参数进行协商。这里需要协商的信息有很多,别的不说,你至少要知道TCP通信过程中的序号是从几开始的。这就是双方协商出来的。比如,如果连接断开重连,旧连接应该被丢弃,而不是继续影响现在的连接。当序列号差异过大就可以判断此时的连接已经不可用,就可以丢弃了。握手过程中确定了初始的序列号,这对于后续的数据传输和有序性非常重要,确保了数据包按正确的顺序传输和接收。
四次握手和两次握手并不是常规情况下使用的连接建立和断开的方式,而是在某些特殊情况下才会使用。
四次握手:一般用于服务器端主动关闭连接,而客户端还有数据需要发送给服务器,服务器会等待客户端发送完数据后才会完成关闭。可以但没必要,两次数据可以合并。
两次握手:不行,存在风险,因为无法确保连接的另一方是否正常。在两次握手情况下,双方都可以发送数据,但无法确认对方是否在线或者是否能够正常接收数据。这可能导致不可靠的连接建立。
因此,通常情况下,三次握手是TCP连接建立的标准过程,确保了连接的可靠性和可用性。
我们刚刚谈论的三个机制都是“可靠性机制”,而不是“安全机制”。安全机制是涉及到“加密”的,TCP的机制主要是为了提供可靠性传输,而不是加密或安全性。而“滑动窗口”是一种“效率机制”。
TCP 的可靠传输是会影响传输效率的,多出一些等待的ACK时间,单位时间内能传输的数据就少了。TCP引入了一些开销,例如确认ACK的延迟、序列号管理等,这对传输效率会产生一些影响。其实TCP只要引入可靠性,传输效率是无法超过没有可靠性的UDP的。TCP的效率机制都是为了让影响更小,缩短和UDP的差距。
滑动窗口就让可靠传输对性能的影响更少一些。这种机制用于优化传输效率,以允许更多数据在网络上传输,同时仍然保持可靠性。
滑动窗口是TCP中的一种流量控制和效率优化机制,它允许发送方连续发送多个数据段而无需等待每个数据段的确认应答。这可以大大提高数据传输的效率,特别是在高延迟网络中。
以下是有关滑动窗口的一些关键概念和原理:
窗口大小(Window Size):窗口大小指的是发送方可以连续发送的数据段数量的上限。例如,如果窗口大小为4,那么发送方可以发送4个数据段,然后等待确认应答,而不必等待每个数据段的确认应答。窗口大小是动态调整的,根据网络条件和接收方的接收能力进行调整。
发送缓冲区(Send Buffer):发送方维护一个发送缓冲区,用于存储已发送但未确认的数据段。窗口大小决定了可以存储在发送缓冲区中的未确认数据段的数量。
接收缓冲区(Receive Buffer):接收方维护一个接收缓冲区,用于存储已接收但尚未按顺序交付给应用程序的数据段。接收方根据数据段的序列号来确定数据段的顺序,并将已接收的数据按顺序交付给应用程序。
滑动窗口的操作:发送方在发送数据段后,将窗口向前滑动,以允许发送更多的数据段。一旦收到确认应答,发送方将已确认的数据段从发送缓冲区中移除,并继续发送下一个数据段。这个过程不断重复,允许高效地传输数据。
流量控制:滑动窗口还充当了一种流量控制机制。接收方可以通过调整窗口大小来控制发送方发送的速率,以确保自己的接收缓冲区不会溢出。如果接收方的接收能力较低,它可以减小窗口大小,从而减缓发送方的速率。
动态调整窗口大小:TCP通过动态调整窗口大小来适应不同的网络条件。较大的窗口大小可以提高吞吐量,但如果出现丢包或延迟,可能需要减小窗口大小以避免过多的重传。
总之,滑动窗口是TCP中的关键机制,用于提高数据传输效率和处理流量控制。它允许发送方连续发送多个数据段,而不必等待每个数据段的确认应答,从而充分利用网络带宽和减少通信延迟。窗口大小的动态调整使TCP能够适应不同的网络条件,确保可靠的数据传输。
刚才我们讨论了确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答。收到ACK后再发送 下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多 个段的等待时间重叠在一起了)。
批量传输数据:
批量传输也不是无限的传输,它也存在一定的上限。达到上限之后再统一等待ACK。不等待的情况下批量最多发的数据量就称为“窗口大小”。
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。
上图的窗口大小就是4000 个字节(四个段)。
发送前四个段的时候,不需要等待任何ACK,直接发送;
那发送第五个段的数据是怎么继续发送的?
是等待四个ACK都回来了再同时发四条,还是收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推?
答案是后者,所以滑动窗的直观效果就是“窗口开始向后滑动”了。
操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答;
只有确认应答过的数据,才能从缓冲区删掉;
窗口越大,则网络的吞吐率就越高;
窗口越大、等待的的ACK越多,传输效率也就越高。
可是不要忘了TCP的初心!它是很看重“可靠传输”的!
上述滑动窗口中,确认应答是可以正常工作的,但是如果出现了丢包怎么办?
它会进行重新传输,但是这里的重传相比于前面的超时重传又有一些新的变化:
这里分两种情况讨论。
情况一:数据包已经抵达,ACK被丢了。
这种情况下,部分ACK丢了并不要紧,我们不需要任何重传,因为可以通过后续的ACK进行确认。
回想一下“确认序号”:
确认序号:
丢失的ACK:
小知识:
"鲁棒" 是一个术语,用来描述系统、算法或协议在面对异常情况或不正常条件时能够继续正常工作的能力。具体来说,"鲁棒性" 表示系统可以在出现错误、噪声、干扰、异常输入或其他不寻常情况下仍能够稳定运行,而不会崩溃或产生不可预测的行为。
在计算机科学和工程领域,鲁棒性是设计和开发系统和软件时非常重要的一个概念,因为现实世界中的系统往往会面临各种不确定性和变化。鲁棒的系统能够容忍这些不确定性,继续提供预期的功能和性能。
在网络通信中,鲁棒的协议或算法可以在网络拥塞、数据包丢失、延迟增加或其他问题的情况下继续提供可靠的通信。TCP协议的鲁棒性使其能够适应各种网络条件,包括丢失的ACK和数据包,以保持连接的可靠性。
"鲁棒" 意味着系统或算法能够在面对各种不确定性和异常情况时表现出稳定和可靠的性能,而不会轻易失败或崩溃。这是计算机科学中一个重要的设计目标。
可能你会问,如果ACK全都丢了呢?
额……这么说吧,平时丢包率10%已经很高了,现在直接丢包率100%,相当于网线都直接断了,更无从谈起“可靠传输”。
情况二:数据包直接丢了。如果ACK全都丢了呢?
此时我们需要主机A去重传数据包,所以主机A就需要知道那个数据丢了,B 也就得告诉A是哪个数据丢了。
上图,由于我们前面11001~2000的数据没了,虽然A已经在发别的数据包了,但是此处的B的ACK仍然是在索要1001,无论当前传输的数据具体范围。
此时A看到B这边连续的几个1001都是在索要1001的数据,A就知道丢失的数据具体是多少,就会重传了1001。
所以当某一段报文段丢失之后,发送端会一直收到 1001 这样的ACK,就像是在提醒发送端 "我想要的是 1001" 一样;
如果发送端主机连续三次收到了同样一个 "1001" 这样的应答,就会将对应的数据 1001 - 2000 重新发送;这个时候接收端收到了 1001 之后,顺利到达,B索要的再次返回的ACK就是7001了(因为2001 -
7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中;
上述重传的过程中并没有额外的冗余操作,哪个数据丢了就重传哪个,没有丢失的数据则不需要重传。整个过程都是比较快速的,是滑动窗口下超时重传的变种。
如果通信双方传输的数据量比较小,也不频繁,就仍然是普通的确认应答和普通的超时重传;
如果通信双方传输的数据量比较大,也比较频繁,就会进入到滑动窗口模式,按照快速重传的方式处理。
这种机制被称为 "高速重发控制"(也叫 “快速重传”)。
通过滑动窗和的方式传输数据,效率是会提升,而且窗口越大,传输效率就越大(等待的ACK更多了,等待的时间更少了)。
那么这意味着滑动窗口设计的越大越好吗?
不一定,如果传输的速度太快,就可能使接收方处理不过来,此时接收方也会出现丢包,发送方还得重传,反而得不偿失。
TCP的前提是“可靠传输”,在可靠性的基础上再提高传输效率。
而接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control:
流量控制就是站在接收方的角度,反向制约发送方的传输速率:发送方发送的速率不应该超过接收方的处理能力。
定义:流量控制(Flow Control)是TCP协议的一个重要机制,用于确保发送端不会以过快的速度发送数据,从而导致接收端无法处理和缓存数据,最终导致数据丢失和网络拥塞。
流量控制的核心思想是,接收端向发送端通知其可接受的数据量,发送端根据这一信息来控制数据的发送速度。
流量控制基本原理:流量控制是一种协议机制,它允许接收方通知发送方自己当前可以接收的数据量。这通过在TCP首部中的"窗口大小"字段来实现。窗口大小表示接收方的缓冲区大小,以字节为单位。
窗口大小通知:接收方将自己可以接收的缓冲区大小放入TCP首部的窗口大小字段,并将这个值随着TCP报文中的ACK一起发送给发送方。发送方通过这个窗口大小字段来了解接收方的接收能力。
动态窗口调整:窗口大小可以根据接收方的缓冲区情况动态调整。如果接收方的缓冲区快满了,它会减小窗口大小通知发送方减缓发送速度。这确保了发送方不会发送太多数据,导致接收方无法处理。
窗口大小扩大因子:窗口大小字段是一个16位的字段,因此理论上最大值为65535字节。但实际上,TCP首部还包括一个窗口扩大因子(Window Scale Factor),用于扩大窗口大小。这意味着实际窗口大小可以更大,以适应高带宽网络。
零窗口:如果接收方的缓冲区已满,它可以将窗口大小设置为0,这告诉发送方暂时停止发送数据。然而,发送方仍然会定期发送窗口探测数据段,以获取最新的窗口大小信息。
数据到达B的系统内核中:TCP SOCKET对象上带有接收缓冲区,A——>B发送的数据就会先到达B的接收缓冲区。B这边还有应用程序,就会调用read这样的方法,把数据从接收缓冲区中读取出来,进一步的处理。
一旦数据被read了,就可以从接收缓冲区删除了。
还记得之前讲到的生产者消费者模型吗?
此时,生产者就是A,消费者就是B的应用程序,里面交易场所就是B的接收缓冲区,相当于一个阻塞队列。
消费能力,就是所谓的“处理能力”,取决于B的应用程序的代码是怎么写的。
那么如何量化衡量处理能力?
我们可以直接通过接受方缓冲区的剩余空间的大小所谓衡量处理能力的指标:
剩余空间越大,意味着消费速度越快,处理能力越强。
剩余空间越小,意味着消费速度越慢,处理能力越弱。
接收方每次收到数据之后就会把接收缓冲区的剩余空间大小通过ACK返回给发送方,发送方就会按照这个数值来调整下一轮的发送速度。
上图,接受缓冲区的总空间4000,收到1000数据之后还剩3000,于是就把3000放到应答报文中的“16位窗口大小”告诉发送方了。
这里的16位窗口大小是否是最大只能表示64K呢?其实不是,TCP报头里的选项部分有一项叫做“窗口扩展因子”。
窗口扩展因子是一个8位的字段,允许窗口大小扩大的倍数。这个扩展因子允许窗口大小扩大到一个很大的范围,以适应高带宽网络和大容量的传输。
窗口扩展因子的工作原理是将窗口大小字段的值左移扩展因子所表示的位数。这就意味着,即使窗口大小字段的默认16位已经能够表示64K,通过窗口扩展因子,TCP可以支持更大的窗口,以实现更高的数据传输速度。
窗口扩展因子的引入是为了让TCP在现代高速网络中能够更好地工作,确保网络吞吐量不会受到窗口大小的限制。这种机制允许TCP在各种网络环境中保持高效和可靠的数据传输。
发送方依据收到的3000确定了这一轮发送的窗口大小。
假设在这个过程里接收方的应用程序还没来得及处理任何数据,此时收到一个数据,接收缓冲区的剩余空间就缩小一分。这些ACK就会通过这种方法计算剩余空间。接收方的缓冲区填满时会反馈0,意味着告诉A,B这边已经满了,你暂时别发数据过来了。此时A就要暂停发送。
暂停多久?
此时虽然不传输业务数据了,仍然会周期性的发送一个“窗口探测包”,并不携带具体的业务数据。探测包就只是为了触发ACK,为了查询当前接收方这边接收缓冲区的剩余空间。
TCP中流量控制和窗口管理的细节:
接收方的缓冲区管理:
窗口探测包:
暂停发送:
这些机制协助TCP实现了流量控制,确保发送方不会发送超过接收方能够处理的数据量。同时,它们也有助于维护连接的可靠性,确保数据不会丢失或被拥塞。这些细节都有助于TCP协议在不同网络条件下高效运行。
流量控制是考虑接收方的处理能力,但是我们不单单只有接收放,还有整个通信的路径。
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的。
拥塞控制是TCP协议中的一种安全机制,用于在网络拥塞情况下避免数据丢失和网络性能下降。
这中间的转发过程中,任何一个节点,处理能力达到上限,都可能会对发送方产生影响,进而影响到可靠传输。
拥塞控制就是考虑/衡量通信过程中中间结点的情况。
拥塞控制的关键问题如下:
接收方的处理能力:接收方的处理能力相对容易量化,通常以缓冲区的大小来表示。接收方可以通过窗口大小字段告知发送方其当前的处理能力。这样,发送方可以根据接收方的能力来决定发送速度。
中间节点的处理复杂性:与接收方不同,中间节点的处理能力和状态更加复杂。因为中间节点包括路由器、交换机等,它们可能会因网络拥塞、队列溢出等原因而导致数据包丢失。中间节点的状态变化和性能难以直接量化。
因此,TCP采用了一种“实验”的方式来找到适当的发送速度和窗口大小:
这种方法将中间节点视为一个整体,并通过实验来发现网络中的瓶颈,从而使数据传输保持在最佳状态,同时减少拥塞和数据丢失。拥塞控制是TCP协议的一项非常重要的功能,有助于确保数据在网络中可靠传输。
在这个过程中,发送方不停的调整窗口大小,逐渐达成“动态平衡”。
这种做法就相当于把中间节点都视为“整体”,通过实验的方式,来找到中间节点的瓶颈在哪里。
TCP引入慢启动机制和拥塞避免机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。以确保在网络拥塞时可以适应和控制数据传输速率,而不会导致网络的进一步拥塞。
传输轮次:当前TCP是第几次发送数据 ;
拥塞窗口:拥塞控制下,发送方应该按照多快的速度(多大的窗口大小)来进行传输。
刚开始会发的非常慢(慢开始/慢启动),后面每次都翻倍,指数增长速度是很快的。在指数增长的过程中如果达到阈值,就要从指数增长变成线性增长。但是线性增长还是在增长,传输速度仍然会越来越快。增长到一定程度网络上就会出现丢包。
所以一旦丢包,就把窗口大小再缩小,重新进行前面的“慢开始——>指数增长——>线性增长”的过程。并且会根据当前丢包的窗口大小重新指定线性增长的阈值(为了避免指数增长一下就达到丢包的极限)
上述拥塞控制是一个经典版本的拥塞控制,后面的TCP又对这里进行了改进。经典版本的“一落千丈”速度直接归零,改进措施不是一下让速度归零,而是仍有一定的初始速度,省略了指数增长的过程,进一步提高了传输效率。
慢启动机制:
初始拥塞窗口大小为1:当TCP连接建立后,初始拥塞窗口大小被设置为1,表示在开始阶段发送少量的数据,以探测当前网络的拥塞状态。
拥塞窗口指数增长:每次收到一个ACK确认应答时,拥塞窗口大小会加倍,即指数级别的增长。这意味着TCP会尝试以较快的速度发送数据。
拥塞窗口与接收窗口比较:在发送数据包之前,TCP会将拥塞窗口大小与接收端主机返回的窗口大小进行比较,选择较小的值作为实际发送的窗口大小。这确保了不会发送太多数据以至于导致拥塞。
像上面这样的拥塞窗口增长速度,是指数级别的。
"慢启动" 只是指初使时慢,但是增长速度非常快。为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。
此处引入一个叫做慢启动的阈值:
慢启动阈值:
慢启动阈值是一个重要的参数,用于控制拥塞窗口的增长速度。当拥塞窗口超过慢启动阈值时,TCP不再采用指数增长,而是采用线性增长。初始时,慢启动阈值被设置为窗口的最大值。
慢启动阈值等于窗口最大值:在TCP连接建立初始阶段,慢启动阈值通常被设置为初始的窗口大小(通常为1),表示在慢启动阶段可以指数级别地增加窗口大小以提高数据传输速率。
超时重发时的慢启动阈值减半:当发生超时重传时,说明网络可能出现了拥塞。此时,慢启动阈值会减半,同时拥塞窗口被设置为1,表示回到慢启动的状态。这是一种拥塞控制机制,以降低数据传输速率,避免进一步加重网络拥塞。
少量丢包触发超时重传:在网络上发生少量丢包时,TCP通常会通过触发超时重传来处理。这是因为少量丢包可能是偶发的,不一定表示网络拥塞,但TCP需要等待一段时间来确认丢失的数据是否已经被接收,从而触发超时重传。
大量丢包认为网络拥塞:如果出现大量丢包,TCP通常会认为网络发生了拥塞,因为这表明网络无法及时处理所有的数据包。在这种情况下,TCP将采取拥塞避免策略来控制传输速率,以减轻网络负载。
逐渐上升和下降的网络吞吐量:随着TCP通信的开始,网络吞吐量通常会逐渐上升,即数据传输速率逐渐增加。但如果网络拥塞或出现丢包,吞吐量会立即下降,以适应当前的网络状况。TCP的拥塞控制机制旨在实现这种动态调整,以保持网络的稳定性和性能。
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
拥塞恢复机制:
如果发生了丢包(包括超时重传触发的丢包),慢启动阈值会变为当前拥塞窗口的一半,同时拥塞窗口被设置为1,表示回到慢启动的状态。这是为了避免过快的增加数据传输速率,因为丢包可能是由于网络拥塞引起的。
拥塞控制的目标是在网络拥塞发生时降低数据传输速率,以避免丢包和网络性能下降。它通过动态调整拥塞窗口大小来实现这一目标,以便在网络负载较重时适当减缓数据传输速率,而在网络状况良好时能够快速提高速率。
都是在限制发送方的发送窗口的大小,最终时机发送的窗口大小失去流量控制和拥塞控制红的窗口的较小值。
延迟应答,是 TCP 协议中的一个优化策略,主要是为了提高网络传输效率。这个策略主要涉及到ACK(确认应答)的发送时机,以及如何调整窗口大小,从而实现更高的传输效率。
正常情况下:A把数据发给B,B就会立即返回ACK给A。
也有时候A传输给B,此时B等一会再返回ACK给A,这就是延时应答。
本质上也是为了提升传输效率。发送方的窗口大小就是传输效率的关键。
流量控制这里就是根据接收缓冲区的剩余空间来决定发送速率的。所以如果有办法能够让这个流量控制得到的窗口更大一点,发送速率就会更快。当然,大窗口的前提是接收方能够处理过来。
延时应答才促成了我们刚刚“四次挥手,三次挥完”。
延迟应答的优化:
窗口大小的影响:
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。
延时返回ACK,给接收方更多的时间来读取接收缓冲区的数据,此时接收方读了这个数据之后,缓冲区剩余空间变大了,返回的窗口大小也就更大了。
延迟应答的影响:
延迟应答和四次挥手:
总之,延迟应答是一项TCP优化策略,它通过合理地延迟ACK的发送,允许接收方更充分地利用其缓冲区空间,从而提高网络传输效率。这个策略对于确保TCP连接的高效和可靠传输非常重要。
避免频繁应答:频繁的ACK应答会导致大量的网络流量,并可能导致网络拥堵。延迟应答通过合并多个应答,可以减少网络流量。
利用缓冲区:当数据到达接收端的缓冲区时,可能缓冲区尚有足够的空间容纳更多的数据。延迟ACK允许接收端有更多的时间来处理这些数据,从而可能在一个ACK中报告更大的窗口大小。
最大化窗口大小:大的窗口可以提供更大的吞吐量。延迟应答通过允许接收端报告更大的窗口来提高传输效率。
一定要记得,窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。
那么所有的包都可以延迟应答么?肯定也不是:
数量限制:接收端不会为每个接收到的数据段立即发送应答。通常情况下,接收端可能会等待接收多个数据段后再发送一个ACK。例如,每隔N个包就应答一次。
时间限制:接收端不会无限期地延迟应答。即使在一段时间内接收端没有收到足够数量的数据段,它也会在达到最大延迟时间后发送ACK。例如,超过200ms就应答一次。
不是所有的数据段都适用于延迟应答:关键的数据段(如终止连接的数据段)可能需要立即应答。
操作系统差异:不同的操作系统和TCP实现可能有不同的延迟应答策略。例如,一般的默认设置可能是每隔2个包应答一次,并在200ms后发送应答。
可能影响其他协议:某些上层协议可能依赖于TCP的及时应答。在这种情况下,延迟应答可能会对这些协议的性能产生负面影响。
综上所述,延迟应答是一个在大多数情况下都很有用的TCP优化策略,但它也需要根据网络条件和应用需求进行适当的调整。
在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 "一发一收" 的,效率不高。
捎带应答就是为了在延迟应答的“一问一答”的基础上进一步提高效率。
ACK是内核立即返回的,response则是应用程序代码来返回的,这两者时机不同。
但是由于TCP引入了延时应答,上面的ACK不一定是立即返回的,可能等一会。
在等待的过程中,B正好就把response计算好了。计算好之后就会把response返回,与此同时顺便就把刚才要返回的ACK也带上了。
这两个数据也就合并成了一个数据。
所以本来是要传输两个TCP数据包(封装分用两遍),目前通过 上述操作就可以把两个包合并成一个了,得到了更高效的效果。
意味着客户端给服务器说了 "How are you",服务器也会给客户端回一个 "Fine, thank you"; 那么这个时候ACK就可以搭顺风车,和服务器回应的 "Fine,thank you" 一起回给客户端。
捎带应答(Piggybacking)是TCP中的一个非常巧妙的效率提升机制。其基本思想是,既然在很多应用场景中数据的交互是双向的,为什么不将应答ACK与即将发送的数据包合并在一起呢?
捎带应答的主要特点和好处:
降低网络负担:通过捎带应答,可以减少网络上的报文数。独立的ACK包与应用数据的返回可以合并为一个数据包进行发送,从而节省带宽。
减少延迟:相比独立的发送ACK包和应用数据,捎带应答可以减少发送的总次数,从而减少网络的往返时间。
提高效率:通过减少单独的ACK数据包,捎带应答机制提高了网络的利用率。
实际运作过程:
当服务器需要响应客户端的消息时(比如上述的 "Fine, thank you"),它会检查是否有待确认的数据。如果有,它会在响应数据包中携带这个确认信息,这样就避免了发送一个独立的ACK数据包。
但值得注意的是,捎带应答并不是始终都能发生的,只有当以下条件都满足时,捎带应答才会起作用:
当这两个条件满足时,服务器会将应答ACK与要发送的数据结合在一个数据包中发送给客户端,实现了应答和数据的合并传输,从而提高了传输效率。
ACK报文: 在TCP通信中,每个接收到的TCP报文段都会触发一个确认应答。这个确认应答报文中包含了一个"ACK"标志位,表示确认应答。当接收方成功接收到数据时,它会发送包含ACK标志的报文给发送方,通知发送方哪些数据已经被接收。
序列号: 每个TCP报文段都包含一个序列号字段,用于标识报文段中包含的数据的位置。发送方使用序列号来编号数据,接收方使用序列号来指示已经接收的数据。
确认号: 每个TCP报文段还包含一个确认号字段,用于指示接收方期望接收的下一个数据的序列号。这个确认号告诉发送方哪些数据已经成功接收,发送方可以据此判断哪些数据需要被重传。
重传机制: 如果发送方在一段时间内(通常是根据RTO确定的)没有收到接收方的确认应答,它会假设该数据包已经丢失,并将其重新发送。这是TCP中实现可靠传输的一部分。
累积确认: TCP的确认应答是累积的,意味着接收方只需确认已接收的数据的最高序列号。例如,如果接收方成功接收了序列号为100、101、102的三个数据包,那么它只会发送一个包含确认号103的ACK报文。这告诉发送方,接收方已经成功接收了序列号为102的数据及之前的所有数据。
缺失的报文段:如果接收方接收到了一个序列号跳跃的报文段,例如,先收到了序列号为100的报文段,然后是序列号为105的报文段,它知道中间的一些报文段(序列号101到104)已经丢失。在这种情况下,接收方会持续地发送确认号为101的应答,即使它已经收到了序列号更大的报文段。
快速重传: TCP还实现了快速重传机制。当接收方检测到失序的数据包时,它可以立即发送重复的ACK,以通知发送方哪些数据包需要重新发送,而不必等待常规的超时触发。
总之,确认应答是TCP用于确保可靠传输的关键机制之一。它通过序列号和确认号来跟踪和确认已经成功接收的数据,以便发送方可以进行重传和流量控制,确保数据能够在网络中可靠传输。这个机制使得TCP在面对网络丢包和延迟等问题时能够保证数据的完整性和可靠性。
这个问题不是TCP独有的,而是面向字节流的机制都有这种情况。
概念:
粘包是指在使用TCP通信时,由于TCP是基于字节流的传输层协议,发送方的多次发送操作有可能被接收方当作一个整体接收,造成多个包数据粘在一起。
此时我们说的“包”时应用层数据包,如果同时有应用层数据包被传输过去此时就容易出现粘包问题。
目前,接收缓冲区中,这三个应用层数据包的数据,就是以字节的形式紧紧挨在一起的。
接收方的应用程序,读取数据的时候, 可以一次读一个字节,也可以读两个字节,也可以读N个字节......
但是我们最终的目标是为了得到完整的应用层数据包。B应用程序,就不知道缓冲区里的数据从哪里到哪里是一个完整的应用数据包了。
为什么会产生粘包问题:
相比之下,像UDP这样的面向数据报的通信方式就不会出现上述问题:
UDP的接收缓冲区中,相当于时一个一个的DatagramPacket的对象,像是一个链表一样。
应用程序读的时候就能明确直到哪里到哪里是一个完整的数据。
解决方法:
核心思路:通过定义好应用层协议,明确应用层数据包之间的边界。
这个时候应用程序读取数据就可以一直持续读到 \n 为止。
我们前面写的回显服务器就是这样的方式。
自定义应用层协议的格式都明确了包的边界:
应用层协议常常需要明确地定义数据的格式和结构,以确保数据在发送和接收时可以被正确地解析和处理。以下是一些常用的应用层数据格式及其特点,这些格式都有助于定义数据包的边界:
XML:
JSON:
Protocol Buffers (protobuf):
在定义自己的应用层协议时,选择合适的数据格式是关键。不同的应用场景可能更适合使用特定的数据格式。例如,Web应用常常使用JSON进行数据交换,而为了高效的数据序列化和传输,许多大规模系统可能会选择protobuf。
八戒吃馒头例子:
想象TCP数据传输如同八戒吃馒头,八戒不知道下一个吃的是一个馒头还是半个馒头,直到他看到馒头之间的绳子(分隔符)或者馒头的大小(固定长度或动态长度)。
思考:UDP是否存在粘包问题?
答案是不会。因为UDP有明确的数据边界。每次交付给应用层的都是完整的UDP数据包。站在应用层的角度,使用UDP时,数据要么完整地被接收,要么根本不被接收,不存在"半个"数据的情况。
如果在使用TCP的过程中出现意外,会如何处理?
在TCP连接中,存在一些异常情况,包括进程终止、机器关机或重启(正常流程)、机器掉电/网线断开(非正常)等,这些情况可能导致连接的异常终止或重新建立。以下是对这些异常情况的更详细描述:
进程终止:当一个进程终止时,它会释放相关的文件描述符,相当于调用了socket.close(),但它仍然可以发送TCP的FIN(结束)包,以通知对端关闭连接。对方收到之后自然会返回FIN和ACK,这边再进行ACK,是正常的四次挥手断开连接。这个情况与正常的连接关闭过程没有太大区别。TCP的连接是可以独立于进程存在的,进程没了,TCP连接不一定。
机器关机:进行关机的时候就是会先触发强制终止进程的操作。此时就会触发FIN,对方收到之后自然会返回FIN和ACK。但是此时不仅仅是进程没了,整个系统也可能关闭。如果在系统关闭之前对端的ACK和FIN到了,系统还是可以进行正常的四次挥手的,但是如果过ACK和FIN迟到了,因为系统已经关闭了,无法进行后续ACK的响应。站在对端的角度,会以为是自己的FIN包丢了,就会重传FIN。重传几次都没有响应,自然就会放弃连接,把持有对端的信息直接删除。
机器重启:机器重启时,TCP连接可能会中断,因为所有的进程和套接字都会重新初始化。在机器重启后,现有的连接通常无法继续。
机器掉电:此时是一瞬间的事情,来不及杀进程,也来不及发送FIN,主机直接停机。站在对端的角度,它不一定知道放生了什么。
如果对端是在发送数据(接收方掉电),发送的数据就会一直等待接收方的ACK。触发超时重传之后就会触发TCP的连接重置功能,就会发送“复位报文段”。
如果复位报文段发过去也没有效果,就会释放连接了。
如果对端是在接收数据(发送方掉电),对端还在等待数据到达。等了好久没有消息,此时它是无法区分:对端没有发送消息还是对方挂了……?TCP提供了一个“心跳包机制”,接收方可以周期性的给发送方发起一个特殊的不携带业务的数据包,并且希望对方返回一个应答。如果对方没有应答,并且重复了多次心跳之后仍然没有应答,就视为“对方挂了”。此时就可以单方面释放连接了。
网线断开:假设A正在给B发送数据,一旦网线断开,A就相当于会触发超时重传,连接重置,单方面释放连接;B触发心跳包,发现对端没响应,单方面释放连接。
在计算机网络中,心跳是为了检测和维持端到端的连接状态。具体地说,一端定期发送小的、固定格式的消息给另一端,如果在预定的时间内没有收到应答,那么就认为连接已经断开或者另一端出现了故障。一般我们后续说到的心跳机制都是在应用程序中自主实现的,而不是直接使用TCP的心跳,因为TCP本身并不提供秒级或毫秒级的心跳检测,而TCP的Keep-Alive机制通常具有较长的时间间隔,用于检测连接是否保持活动。我们往往希望是秒级/毫秒级就能检测出对端是否存活,所以在需要更快速的心跳检测时,一般需要应用层自主实现心跳机制,以满足秒级或毫秒级的要求。
TCP的保活机制:TCP确实有一个叫做“keep-alive”的机制。它在连接上没有数据传输时,会周期性地发送探测消息来检查对方是否还在。但这个机制默认的时间间隔通常很长,可能是小时级别。
应用层心跳:由于TCP的保活机制不够灵活,很多应用选择在应用层实现自己的心跳机制。这样的好处是心跳间隔、超时时间和重试次数都可以按照应用的需求进行定制。
应用场景:在一些需要高可用性的系统中,如分布式系统、在线游戏、实时通讯应用等,毫秒级的延迟都可能导致问题,因此这些应用通常会选择在应用层实现心跳,以便更快地检测到对方的故障。
优化考虑:频繁的心跳会增加网络和系统的负担。因此,在实际应用中,需要根据系统的实际需求来权衡心跳的频率和超时时间。
总的来说,虽然TCP提供了保活机制,但很多应用更偏向于在应用层自定义心跳,这样可以更灵活地满足应用的特定需求。
以下是应用层通常采用的一些方式来实现快速心跳检测:
应用层心跳包:应用程序定期发送特定格式的心跳包给对端,然后等待对端响应。如果在规定时间内没有收到响应,应用程序可以判定对端已经不再存活。
PING检测:在某些情况下,可以使用操作系统提供的PING工具来检测目标主机是否存活。虽然PING是基于ICMP协议的,不同于TCP,但它可以提供较快速的存活检测。
HTTP请求:在HTTP应用中,可以使用HTTP HEAD请求或OPTIONS请求来检测远程服务器是否存活。这可以用来监测Web服务器的可用性。
定时任务:在某些情况下,可以使用操作系统级别的定时任务或调度任务来运行应用程序的心跳检测功能。
基于UDP的心跳:UDP协议允许较快的通信,因此可以实现较快速的心跳检测。应用程序可以定期发送UDP数据包,并等待响应。
总之,TCP在处理异常情况时通常会尽力确保连接的稳定性,同时也允许各种情况下的连接恢复和重建。
TCP不只有我们介绍的这十个较核心的机制,还有很多其他机制:
拥塞控制算法:TCP有不同的拥塞控制算法,如拥塞避免、快速重传、快速恢复等,用于控制网络拥塞情况下的数据传输速度。
窗口缩放选项:TCP窗口大小可通过选项进行缩放,以便支持更大的窗口。
选择确认:TCP可以使用选择确认(SACK)来指示哪些数据块丢失,以加速重传。
时间戳选项:时间戳选项允许记录TCP报文的发送和接收时间,以帮助计算往返时间和更好地处理延迟。
最大报文段长度(MSS)选项:MSS选项允许协商每个TCP报文段的最大长度,以适应网络的MTU(最大传输单元)。
紧急数据:TCP支持紧急数据传输,允许发送方将重要数据标记为紧急,以快速处理。
TCP压缩:TCP/IP协议族中的一些变种支持数据压缩,以减小数据传输的开销。
最大连接数限制:TCP/IP协议栈通常有最大并发连接数的限制,可以根据需要进行调整。
这些机制和选项允许TCP更好地适应不同网络条件和应用需求。TCP协议的灵活性和可调整性使其成为Internet上最常用的传输层协议之一。
TCP(传输控制协议):
UDP(用户数据报协议):
TCP适用于绝大部分场景,
而UDP适用于对“可靠性不敏感,但性能敏感”的场景。例如:UDP会用在局域网内部(同一个机房)的主机之间的通信。同一个机房内部网络结构简单,带宽充足,交换机/路由器网络设备负载程度也不是很高,出现丢包的概率不大,往往我们也希望机器之间的数据传输更快。
还有,如果要传输比较大的数据包,TCP优先(UDP的64KB的限制),但是如果要进行“广播传输”,就优先考虑UDP。因为UDP天然支持广播,而TCP不支持(应用程序额外写代码实现):
广播是一种特殊的通信场景,它需要将数据广播给局域网中的所有主机,而不是仅限于单个接收方。这种情况通常用于应用程序或功能,例如投屏功能,其中一个主机(例如投影仪或其他设备)需要将其屏幕上的内容广播到局域网中的所有设备。
在这种情况下,UDP通常是更合适的协议选择,因为UDP天然支持广播和多播传输。UDP数据包可以直接发送到局域网中的广播地址或多播组地址,使得所有监听该地址的设备都能接收到数据。这在实现投屏、游戏多播、多播音频/视频流等应用中非常有用。
需要注意的是,TCP通常不直接支持广播,因为它是面向连接的协议,连接通常是一对一的。在需要广播的情况下,通常要依赖UDP来实现这一功能。
总之,选择TCP还是UDP取决于应用需求。在绝大多数情况下,TCP是首选,因为它提供可靠性,确保数据的完整性。但对于特定的性能敏感或实时应用,UDP可以是更好的选择,尤其是在可靠性要求较低的情况下。如果需要同时使用TCP和UDP,应用程序可以根据场景选择合适的协议。
经典面试题:如何基于UDP实现可靠传输?
这个问题其实是在考察TCP!口嗨,把关于TCP的相关特点答上就行。
网络层要做的事情主要是两方面:
地址管理:网络层通过协议(如IP协议)定义了一套地址分配和管理规则,以确保每个设备在网络中具有唯一的标识。这些规则包括IP地址的分配方式,地址的结构,以及如何解析和识别这些地址。IP地址充当了设备在网络中的“邮寄地址”,它们用于标识设备的位置,以便能够正确地传递数据。
路由选择:网络层还涉及了路由选择,即确定数据包应该从源地址传输到目标地址的路径。这个路径可能涉及多个中间节点,例如路由器或交换机,而网络层需要使用路由选择算法来决定最佳的路径。路由选择算法通常考虑因素包括路径的成本、距离、拓扑结构和负载等,以确保数据包以最有效的方式传输。
通过地址管理和路由选择,网络层帮助构建了复杂的全球互联网络,为不同设备提供了通信的框架,同时保证了数据的正确传递和网络资源的高效利用。这对于现代互联网的运行至关重要。
协议头格式如下:
版本号(Version):指定IP协议的版本,对于IPv4来说,版本号是4。如果是IPv6,版本号就是 6。大规模使用的版本就是这两种。
头部长度(Header Length):IP头部的长度,以32位字长为单位表示,最大为60字节。IP协议的报头也是变长的。0~0xf=>*4---->0~60.
服务类型(Type Of Service,TOS):这字段已经废弃,但仍然包括3位的优先权字段和4位的TOS字段,以及1位的保留字段。4位的TOS字段通常表示以下四个参数中的一个:最小延时、最大吞吐量、最高可靠性、最小成本。这四个位彼此之间是冲突的,只有一位设为1。不同的位设为1,表示IP协议不同的形态,它们可以相互切换。
16位总长度(Total Length):表示整个IP数据包的长度,以字节为单位。描述了IP数据包最长是多长。IP协议确实也存在64KB这样的限制,但是它自身支持拆包组包的功能。通过接下来的三个属性,来支持IP协议的拆包和组包。
16位标识(ID):一个唯一的标识符,用于标识主机发送的数据报文。如果IP报文在数据链路层被分片,每个分片都有相同的ID。如果一个大的数据包需要拆分为多个小包,此时拆分出来的者多个小包。标识就是相同的数值。
标志字段(Flags):包括3位,其中第一位是保留位,第二位为1表示禁止分片,第三位表示更多分片,最后一个分片的这一位被设置为1,其他分片设置为0。(其实只有两位有效:有一位表示是否允许拆包。还有一位表示是否是最后一个包。)
分片偏移(Fragment Offset):表示分片相对于原始IP报文的偏移量,以8字节为单位,用于指示当前分片在原报文中的位置。因此,分片长度除了最后一个报文外,都必须是8的整数倍。描述了当前每个小的数据包(分片)的相对位置。
生存时间(Time To Live,TTL):描述了这个IP数据包在网络上还能存活多久。TTL表示数据报文达到目的地的最大跳数,初始值一般会被设置好(32、64、128……)。再转发过程中,每经过一个路由包,TTL减1,当TTL减至0时,数据报文将被丢弃,主要用于防止出现路由循环或者不存在的IP地址。
8位协议:描述的是IP数据包的载荷部分,是一个UDP数据包还是一个TCP数据包?即表示上层传输协议的类型,例如TCP(6)或UDP(17)。
16位头部校验和(Header Checksum):这个校验和只校验IP首部,不考虑数据的载荷。其实也没关系,因为这个数据其实就是UDP/TCP这样的数据,它们自身都是有校验和的。使用CRC进行校验,以检测头部是否被损坏。
32位源地址(Source Address):IP数据包中最关键的内容。32位字段,表示发送端的IP地址。IP地址本质上就是一个32位的整数,为了方便人来理解,通常表示为四个用点分隔的十进制数。每个点分十进制数的范围是0到255,因为一个8位二进制数可以表示的最大值是255。这种表示方法被广泛应用,例如IPv4地址的表示方式就是如此,例如:192.168.1.1。这四个十进制数代表了IP地址中的四个八位字节。
32位目标地址(Destination Address):32位字段,表示接收端的IP地址。
选项字段:这是一个不定长字段,最多可达40字节。通常包括一些可选的控制和参数设置。
1. 概念
IP地址 (Internet Protocol Address),亦译为网际协议地址,是计算机网络中设备的唯一标识。
2. 作用
3. 格式
4. IP版本
5. IP地址的组成
6. IP地址分类
“上古时期”的网段划分方式:
过去,IP地址分为A、B、C、D、E五类:
备注:主机最大连接数减去2,是扣除主机号为全0和全1的特殊IP地址。
过去的 A、B、C、D、E 类地址是一种旧的分类方式,而私有地址和公有地址是一种更现代、更灵活的方式来管理 IP 地址,使其适用于内部网络和全球互联网之间的通信。私有地址的引入有助于更好地解决IP地址不足的问题。
过去的IP地址分类(A、B、C、D、E)与私有地址和公有地址之间存在关联,但它们有一些区别和细微的不同:
A、B、C、D、E 类地址是历史上的一种分类方式,用于标识 IP 地址的范围和用途。这些分类主要用于全球互联网的路由和分配,以便更好地管理地址空间。在这个分类中,A 类地址用于大型网络,B 类地址用于中型网络,C 类地址用于小型网络,D 类地址用于多播,而 E 类地址用于实验。
私有地址和公有地址是一个更现代的概念,用于更好地管理内部局域网(LAN)与全球互联网之间的通信。私有地址范围用于内部局域网通信,而公有地址是用于在全球互联网上唯一标识设备和服务器的地址。私有地址的分类不依赖于旧的 A、B、C 类分类,而是由特定的CIDR地址块组成,例如 10.0.0.0/8、172.16.0.0/12 和 192.168.0.0/16。
私有地址的引入更灵活,允许内部网络在多个子网中使用相同的私有地址,而不受全球互联网的影响。这有助于降低 IP 地址耗尽的影响,提供更好的网络管理和隔离。
私有地址和公有地址:
私有地址和公有地址是 IP 地址的两种分类,它们用于标识计算机或设备在网络中的位置。这些分类有助于在互联网上进行网络地址分配和路由。
私有地址(Private Address):
公有地址(Public Address):
总之,私有地址用于内部局域网通信,而公有地址用于全球互联网通信。路由器通常用于转换内部私有地址和外部公有地址之间的流量,以便内部设备可以与互联网上的其他设备进行通信。
除了传统的 A、B、C、D、E 类地址和私有地址/公有地址分类外,还有其他一些分类方法和概念,用于更精细地管理 IP 地址和满足不同网络需求。以下是其中一些常见的分类和概念:
子网掩码(Subnetting):子网掩码是一种方法,通过它可以将 IP 地址划分为更小的子网。这有助于实现网络分段、提高网络性能和安全性。子网掩码的使用可以让网络管理员更灵活地分配 IP 地址。
CIDR(Classless Inter-Domain Routing):CIDR 是一种现代的 IP 地址分类方法,用于更精细地分配 IP 地址。它使用前缀长度表示网络的范围,例如,192.168.0.0/24 表示一个拥有256个 IP 地址的子网。CIDR 可以减少地址浪费,并更好地支持可扩展的网络。
IPv6 地址:IPv6 是 IPv4 的下一代协议,它使用128位地址,提供了远远超过 IPv4 的地址空间。IPv6 地址通常不使用类似 A、B、C 的分类,而是以更简单和灵活的方式分配。
公有 IP 和私有 IP 地址:如前所述,公有 IP 地址用于全球互联网上唯一标识设备,而私有 IP 地址用于内部局域网通信。
静态 IP 和动态 IP:静态 IP 是指网络设备拥有固定的 IP 地址,不会更改。动态 IP 是指设备在每次连接网络时会分配一个不同的 IP 地址。动态 IP 通常由 Internet Service Provider (ISP) 分配。
这些分类方法和概念都有不同的用途,可以根据网络需求和管理目标来选择合适的分类方式。
7. 特殊IP地址
IP地址的分类与浪费问题
在上述的分类中,存在IP地址浪费的问题:
B类网络的浪费:许多单位倾向于申请B类网络,因为C类网络能连接的主机数量有限。但在实际网络架设中,很少有单位能连接近65534台主机(B类网络的最大主机数),从而造成大量的IP地址浪费。
A类网络的浪费:相似地,A类网络的IP地址也经常被大量浪费,因为其可以连接的主机数量远超多数组织的需求。
子单位分配问题:当一个单位拥有一个网络号并希望将其下的IP地址分给其下属的小单位时,如果选择申请新的网络,同样会造成浪费。
为了缓解这些问题,网络技术人员引入了子网掩码来进行子网的划分。
IP地址可以被划分为两个部分:
两个基本原则:
在同一个局域网内,所有设备的网络号必须是相同的,而主机号必须是独特的。
若两个局域网通过同一个路由器相连接,那么它们必须拥有不同的网络号,以避免地址冲突。
其实一个IP地址,哪个部分是网络号,哪个部分是主机号是不确定的。
子网掩码就是用来确定网络号的。
子网掩码
格式:
功能与应用:
划分不同类别的IP地址子网:例如,对于B类IP地址191.100.0.0,原来是由16位的网络号和16位的主机号组成的。如果我们使用子网掩码255.255.128.0(或称为 /17),那么网络部分将会是17位,将原本的主机部分进一步细分为1位子网号和15位主机号。
网络通信中的应用:在网络通信中,子网掩码与IP地址结合可以计算得到网络号和主机号。这在确定数据报文的传输路径,特别是判断目的主机是否在同一个子网中时,尤为重要。
计算方法:
通过对IP地址和子网掩码进行“按位与”操作,我们可以得到网络号。
如果我们取子网掩码的按位反(例如,从255.255.255.0变为0.0.0.255)并与IP地址进行“按位与”操作,结果将是主机号。
通过这种方式,子网划分和子网掩码的使用大大提高了IP地址的使用效率,缓解了地址资源的浪费问题
上面带有子网掩码的网段划分是当下网络环境的现状,还记得我们在IP地址分类那里提到的上古时期的网段划分嘛?你现在应该能够理解了。
还有我们提到的一些特殊的IP地址:
IPv4地址的32位源地址中的32位整数表示的数据范围是42亿9千万。
随着互联网的快速发展,连接到网络的设备数量逐渐增多,原本的IPv4地址已经不能满足当前的需求。为了解决这个问题,产生了以下的方案:
概述:NAT允许局域网内的多个设备共享一个公网IP地址。当局域网内的设备访问外网时,他们的内部IP地址会被替换为该公网IP地址。
内网与外网IP:
NAT工作原理:
优点:使得多个设备可以共享一个外部IP地址,从而大大减少了公网IP地址的需求。
缺点:NAT可以导致某些应用程序和服务的不正常工作,例如Peer-to-Peer应用和VoIP服务。
NAT机制具体是怎么工作的?
此时运营商路由器也是一个NCT设备,就能够对当前这里的源IP进行替换(内网IP无法在广域网上使用)转换成外网IP。
站在人家的服务器的角度,看到的源IP就是我的电脑被替换后的IP。
刚刚的请求现在已经到达服务器,服务器现在想要返回一个响应,就会构造一个数据包:
这个数据包转换回来的时候,又会经过运营商路由器。
运营商路由器内部会记录一个映射关系。刚才是把192.168.0.233替换成了121.229.106.113。现在响应报文回来了,就需要把IP替换回去。
此时运营商路由器NCT设备就相当于一个中转站。
那现在问题来了,如果当前局域网内,有多个IP想要访问同一个网站服务器,此时服务器返回的响应,经过路由器之后,要交还给哪个主机呢?
这里其实是要结合端口号来进行区分的。端口号不仅可以用来区分同一个主机的不同进程,还可以区分不同主机的不同进程。
虽然IP一样,但是这两个请求来自于不同的端口,返回的响应数据自然也会带有不同的目的端口(也就是替换前的源端口)。
服务器返回的两条数据:一个目的端口是10001,另一个是666。路由器就知道了,10001 的这个需要把IP替换回第一个主机的IP,666的这个需要把IP替换回第二个主机的IP。
那么万一两个主机又拿到了同一个端口号怎么办?到达路由器之后,路由器会发现这两个数据包的源IP和目的端口都一样,代表访问同一个服务器。同时源端口也一样!
这个时候路由器就可以主动把相同的端口替换成不同的端口。
所以当前网络环境就是以NAT+动态分配的方式来解决IP地址不够用的问题。
NAT机制最大的优势:它是一个纯软件的方案。
NAT的优势:
纯软件方案:NAT不需要硬件支持,只需在路由器或防火墙上实施相应的软件功能。这使得NAT的实现和部署变得相对简单和成本效益高。
节省IP地址:通过NAT,一个公共IP地址可以为数百或数千的私有IP地址服务,大大减少了对公共IP地址的需求。
增加安全性:NAT为私有网络提供了一个安全的屏障。外部的攻击者很难直接攻击局域网内的设备,因为它们隐藏在私有IP地址后面。由于外部设备不能直接访问到局域网内的私有IP地址,除非局域网内的设备首先发起请求,这为内部设备提供了额外的安全层。
NAT的局限性:
不透明:由于地址转换,端到端的通信不再是透明的。这可能会导致某些应用程序或协议工作不正常。
限制了外部设备的访问:虽然这提供了安全性,但在某些情况下,可能需要外部设备能够访问内部网络的资源。为了实现这一点,通常需要配置端口转发或其他技术。
有状态操作:NAT需要跟踪所有活动的连接,这使得NAT成为一个有状态的操作,需要更多的计算和内存资源。
正是因为这个机制,导致局域网内部的数据能够主动访问外网的设备,而外网的设备是无法主动访问局域网内部的设备的。这个局限性也不是坏事,反而更好的保护了电脑的安全。
IPv6使用了16个字节来表示IP地址。
IPv6比IPv4的地址多了多少?
这里其实是一个指数关系,2^32-->2^128=2^32*2^32*2^32*2^32,最终的结果是一个天文数字!它可以给地球上的每一粒沙子都分配一个不同的IP地址,甚至还绰绰有余!
但是事实上,IPv6的普及度比较低。尽管它但是的时间和NAT差不多,都是上个世纪90年代出现的。但是NAT一经问世,立马就被广泛使用,而IPv6一直不温不火,主要原因是它和IPv4不兼容(以前的路由器都是支持IPv4的),需要网络设备的支持和过渡。想要做到支持IPv6就需要换设备,但是老百姓们钱花出去了没有什么收益,网速又不会变快,得不偿失……
总结: 当前的网络环境往往采用NAT与动态IP地址分配相结合的方式来解决IP地址短缺的问题。这两种方法都可以提高IP地址的利用率,但都不能真正解决IP地址数量不足的问题。IPv6的推广使用可能是一个更为长远的解决方案,它提供了几乎无限的IP地址空间。
上述和IP地址相关的规则,也是IP协议的一部分,它有两个核心功能:地址管理和路由选择。
看完了地址管理,那么就应该来看看路由选择。
路由选择就是表述了IP协议(IP数据报)的转发过程。
IP路由选择的过程可以大致描述为一个网络中的路由器决定应该将数据包传送到哪个下一个路由器或目标主机。
我们使用导航APP的时候会发现,你可以选择“路径最短”、“耗时最短”或者“花费最小”的路径,这是这个APP通过卫星定位等分析数据,站在全局的角度上看问题。
但是当我们进行IP数据报的时候,每个路由器都无法知道网络的“全貌”,只知道一些局部信息,比如“哪些设备是和它相连的”。这就意味着,IP数据再转发过程中是一个“探索式”、“启发式”的过程,所以这个过程中它很难给出“最优解”,只能是一个“较优解”。
我们刚刚提到的是互联网的一大核心原理,即分布式决策和路由选择。
下面,我们来详细探讨一下:
局部知识与全局知识:
动态路由选择:
“探索式”与“启发式”:
“最优”与“较优”:
复杂性与鲁棒性:
路由器转发数据报的过程比较复杂,我们先来看个例子:
想象你在一个庞大的古老城市中,这个城市就像互联网。每个城市的交叉口都像一个路由器,而各条道路就像互联网上的连接。你(数据报)想从城市的一边到达另一边的某个位置(目标IP)。你没有完整的地图,但每个交叉口(路由器)都有一个标志(路由表)告诉你如何到达附近的区域。
路由选择就像在一个大城市中找到一个目的地,尽管每个交叉口只提供有限的指导,但最终通过各种标志和指示,你将找到通往目的地的路径。路由器的工作原理也是如此,它们每次只做出基于当前信息的最佳决策,希望最终能够成功地将数据报传送到其目标地址。
那么把核心思想抽离出来再看看:
假设有一个包含多个路由器的网络,其中每个路由器都连接到其他路由器和终端设备。现在,某个终端设备A想要向终端设备B发送数据包。路由选择的过程如下:
设备A发送数据包:设备A知道它要发送数据包给设备B,但它不知道设备B的物理位置,因此将数据包发送给与设备A直接相连的路由器R1。
路由器R1的决策:路由器R1接收到数据包后,检查其路由表,看是否知道如何到达设备B。如果R1知道如何到达B,它将直接将数据包发送给路由器R2,因为R2是通往B的下一个跳(hop)。
路由器R2的决策:路由器R2接收到数据包后,也检查其路由表。如果它知道如何到达设备B,它将直接将数据包发送给B。如果R2不知道如何到达B,它将尝试将数据包传递给其他路由器,一直到找到通向B的路径或者放弃。
最终到达设备B:数据包可能会在网络中经过多个路由器,每个路由器都根据其路由表和路由选择算法来决定下一个跳。最终,数据包将到达设备B。
在实际网络中,路由选择涉及更多的复杂性,因为路由器需要考虑不同的因素,如距离、成本、拥塞情况等。路由选择算法的目标是找到一条较优的路径,以便数据包可以快速、可靠地到达目标设备。这是网络通信中的重要环节,确保数据包能够有效地从源到目标传输。
路由选择的详细过程:
数据包生成:路由选择开始于源设备,这是一个生成要发送的数据包的地方。源设备知道目标设备的IP地址,但通常不知道目标设备的物理位置。
查找下一跳:源设备首先查找自己的路由表,以确定数据包应该发送到哪个路由器,这个路由器被称为下一跳。这通常是默认网关,它将数据包引导进网络。
数据包发送:源设备将数据包发送到下一跳路由器。这个路由器可能与源设备直接相连,也可能需要通过其他路由器来达到。
路由器的路由表:每个路由器都维护着一个路由表,其中包含了它知道的网络拓扑和如何到达其他网络的信息。路由表包含目标网络的IP地址、子网掩码和下一个跳的信息。
目标网络的查找:当数据包到达下一个路由器,它查找自己的路由表来确定如何到达目标网络。这可能需要多次查找和决策。
路由选择算法:路由器使用路由选择算法来确定下一个跳。这个算法可以是静态的(管理员手动配置)或动态的(路由器之间交换信息以动态更新路由表)。
拥塞和负载平衡:路由选择算法还需要考虑拥塞情况和负载平衡。路由器可能会选择较少拥塞或负载较低的路径。
传输到下一个路由器:路由器将数据包传递给下一个跳,这个过程一直持续到数据包到达目标网络的路由器。
最终到达目标:数据包最终到达目标网络的路由器,该路由器知道如何将数据包传递给目标设备。
数据包交付:目标路由器将数据包传递给目标设备,完成数据包的传输。
路由表的生成和更新:
总的来说,路由选择是一个复杂的决策过程,路由器需要根据路由表中的信息、路由选择算法、网络拓扑和实时条件来决定数据包的下一跳。这确保了数据包可以快速、可靠地到达其目标,同时充分利用网络资源。路由选择在互联网和大型企业网络中发挥着至关重要的作用,是网络通信的基础。
DNS,也称为域名系统,是一个应用层的协议,而且它是整个互联网工作的关键。
我们通常使用IP地址来描述设备在网络上的位置,不方便记忆和传播,不适合进行宣传。因此,考虑到IP地址是机器友好的,但对于人类来说却是难以记忆的,我们就需要一种更直观、更易于理解和记忆的方式来表示这些IP地址,这就是域名(使用表示实际意义的单词)。
我们需要有一套自动的系统把域名翻译成IP地址,你可以把域名和IP想象成一组键值对。
例如,比起告诉别人一个IP地址如“192.168.1.1”,告诉他们访问“example.com”要容易得多。
最早的域名解析系统是通过一个简单的文件来实现的,hosts文件。
早期的解决方案:hosts文件
在DNS被广泛接受和使用之前,主机名到IP地址的映射是通过hosts文件来完成的。每台计算机上都有一个hosts文件,里面列出了与其对应的IP地址。但这种方法有明显的缺点:
很明显,这种方法不是长久之计。
一般来说,hosts文件在这里:(但可能根据系统不同而有差别)
但是这里真的打开hosts文件大概率是空的,这个机械已经不再使用了,只有偶尔进行一些测试工作的时候需要手动编辑这里。hosts文件来维护域名和IP的映射关系,非常不方便。
DNS的崛起
为了解决上述问题,DNS应运而生。简单来说,DNS是一个分布式的数据库系统,存储了域名和对应的IP地址。当你尝试访问一个域名时,你的设备会首先查询DNS服务器,找出该域名对应的IP地址,然后才能够访问该网站。
这种系统的优势很明显:
于是就有大佬搭建了一套DNS系统服务器,把映射关系保存到这个服务器里面了。
如果你想访问某个域名,就先给这个DNS服务器发起请求,查询一下当前的域名对应的IP,然后再访问目标网站。后续如果有域名的更新,只需要更行这一组的指定服务器即可,不需要修改每个用户电脑的hosts。
那么问题来了,全世界无时不刻都有很多设备需要进行DNS请求,这一组DNS服务器能支持那么多请求量吗?硬件资源再多也架不住那么消耗啊!
这种所谓的高并发问题,千万不要想得太复杂
高并发问题核心思路就是两个:
1、开源:搭建DNS系统的大佬们就开始号召各个网络运营商,搭建自己的一组“DNS镜像服务器”。并且镜像服务器的数据都从DNS系统(根)这里同步,此时用户就会有先访问离自己最近的镜像服务器。这意味着,当用户尝试解析一个域名时,他们会首先连接到离他们最近的服务器,这大大减少了中央服务器的负担。
2、节流:让请求量变少,上每个上网的设备进行本地缓存。第一次请求DNS即可,把请求的结果保存到本地,后续请求都可使用第一次的。
本地缓存:绝大多数操作系统都在本地进行DNS缓存,以减少对远程DNS服务器的请求。
TTL (Time To Live):DNS记录都有一个TTL值,指示记录在客户端或其他DNS服务器上缓存多长时间。这可以大大减少对权威DNS服务器的请求量。
任播(Anycast):这是一个网络寻址和路由方法,使得单一的目标地址可以存在于多个网络位置。多个DNS服务器可以共享相同的IP地址,确保用户请求被路由到最近的服务器。
所以,随着互联网的发展,单一的DNS服务器也无法满足所有的请求。
全球有无数的设备需要进行DNS请求,那么如何处理这种大规模的请求呢?
答案是DNS的分层和分布式结构。DNS采用了分层的架构,包括根服务器、顶级域服务器和权威服务器。而且,每一层都有多个服务器,分散在全球各地,以确保快速、准确和可靠的响应。
此外,为了进一步提高效率和速度,DNS还使用了缓存技术。这意味着当一个DNS查询被完成后,结果会被暂存,以便下次快速提供答案,而无需重新查询整个DNS系统。
总之,DNS是互联网的基石之一,它解决了将机器友好的IP地址转换为人类可读的域名的问题。通过分布式、层次化的结构和先进的缓存技术,DNS确保了互联网的流畅运行和稳定性。
还有一些细节问题:
一个常见的情况是,在DNS解析过程中,访问就近的运营商的镜像服务器可能会出现问题。这可能是因为运营商的镜像服务器遇到了网络故障、负载过重、配置错误或其他问题,导致它无法提供有效的DNS解析服务。
当这种情况发生时,可能会导致一些网络问题,例如:
访问延迟:如果就近的运营商的镜像服务器无法响应DNS解析请求,客户端可能需要等待更长时间才能获取IP地址,从而导致访问延迟。
无法访问网站:如果DNS解析失败或超时,客户端可能无法获得目标网站的IP地址,从而无法访问网站。
为了减轻这种问题,网络运营商通常会采取以下一些策略:
多DNS服务器:运营商通常维护多个DNS服务器,以分散负载和提高冗余。这些服务器通常分布在不同地理位置,以提供更好的可用性。
DNS缓存:DNS服务器通常会缓存已解析的域名和IP地址,以减轻对根DNS服务器的请求负载。这使得大多数DNS解析可以在本地或运营商级别解决,而不需要访问根DNS服务器。
DNS Anycast:一些运营商使用DNS Anycast技术,它允许多个服务器使用相同的IP地址,但位于不同位置。这样,客户端将请求发送到最近的可用服务器,从而提高性能和可用性。
虽然就近的运营商的镜像服务器可能会遇到问题,但这些策略有助于减轻问题,并确保较好的DNS解析服务。同时,DNS是互联网的关键基础设施,因此网络运营商和互联网服务提供商通常会投入大量资源来确保其可靠性。
DNS查询是解析域名(如www.example.com)以获取相应IP地址的过程。查询是递归的,通常由你的设备或本地DNS服务器触发,遵循以下一般步骤:
本地DNS缓存查询:当你的设备首次尝试访问一个域名时,它将首先检查本地DNS缓存,这是设备上存储的最近查询结果的地方。如果该查询之前已经在本地DNS服务器中解析过,结果将被直接返回。
本地DNS服务器查询:如果本地缓存中没有找到查询结果,你的设备会将查询发送到本地DNS服务器,通常由你的互联网服务提供商(ISP)提供。本地DNS服务器会首先检查自己的缓存,如果找到则返回结果,否则它会继续查询。
根服务器查询:如果本地DNS服务器没有查询结果,它会向根服务器发送查询请求。根服务器位于DNS层次结构的顶层,负责提供顶级域(如.com、.org)的DNS服务器的IP地址。根服务器返回TLD服务器的IP地址,以便下一步查询。
TLD服务器查询:本地DNS服务器接着向TLD服务器发送查询请求,请求特定顶级域(如.com)的权威服务器的IP地址。TLD服务器返回权威服务器的IP地址。
权威服务器查询:最终,本地DNS服务器发送查询请求到特定域名的权威服务器。权威服务器是存储特定域名解析信息的服务器,它返回所请求域名的IP地址。
本地DNS服务器响应:本地DNS服务器接收到IP地址后,它将结果缓存并返回给你的设备。
设备响应:你的设备现在知道了域名对应的IP地址,它将使用该IP地址来建立连接并访问所需的资源(如网站)。
域名系统使用层次结构来组织域名。域名的不同部分用点号分隔,最右边的部分是顶级域(TLD),而最左边的部分是主机名。几级域名是指域名中的层级数量。以下是一些示例:
一级域名:这是顶级域(TLD),如.com、.org、.net等。一级域名通常表示域名的整体性质,例如商业(.com)、组织(.org)或网络(.net)性质。
二级域名:这是紧随在一级域名后的部分,例如example.com中的"example"。通常,二级域名用于表示特定组织、公司或网站。
三级域名:这是再次分隔的域名部分,如www.example.com中的"www"。三级域名可以用于指定特定服务或子域。
更多级别:您可以有更多级别的域名,例如subdomain.example.com,其中"subdomain"是四级域名。
每一级域名都可以有自己的DNS记录,以便将子域名指向不同的IP地址或服务器。这使得域名系统非常灵活,使网站和服务能够以多样化的方式组织和管理其域名结构。
包括我们之前说到的IPv6,它需要新的DNS解析,这对于我们国家来说也是一个新机会,毕竟IPv4的DNS掌握在美国的手里。
新的地址空间:IPv6为每个设备提供了更多的独特地址,这确实会对DNS的负荷产生一些影响。
新的DNS解析:所以IPv6需要进行新的类型的DNS解析(AAAA记录而不是A记录)。
数据链路层是OSI模型中的第二层,它负责将数据包从网络层传输到物理层,以便在物理媒体上进行传输。
数据链路层的主要职责:
封装和解封装:数据链路层将网络层的数据包封装为帧,以便在物理媒体上传输。它还负责解封装接收到的帧,将其传递给网络层进行处理。
地址分配:每个网络接口(如网络适配器或网卡)都有一个唯一的物理地址(MAC地址),数据链路层用于识别和寻址设备。MAC地址是数据链路层的地址。
错误检测和纠正:数据链路层检测和可能纠正传输过程中发生的错误。这确保了数据的完整性。
流量控制:数据链路层可以通过一些机制控制数据的传输速率,以防止拥塞和数据包丢失。
访问控制:数据链路层可以控制多个设备如何共享相同的物理媒体。不同的访问控制协议允许设备以不同的方式共享带宽。
在数据链路层,也有很多种协议,其中一个比较常见、常用的就是“以太网协议”。
通过网线/光纤来通信,使用的协议就是以太网协议。以太网是横跨数据链路层和物理层。
以太网是一种局域网技术,它定义了一系列的数据链路层和物理层标准,包括网络拓扑结构、数据帧格式、访问控制机制、传输速率等。
以太网是最常见的数据链路层协议,它用于有线(以太网)和无线(Wi-Fi)局域网络。
以太网协议的特点包括:
以太网是因特网的基础,它在许多局域网络和广域网络中广泛使用。除以太网外,还有其他数据链路层协议,如PPP(Point-to-Point Protocol)用于串行连接,HDLC(High-Level Data Link Control)用于广域网连接,以及Wi-Fi协议用于无线局域网络等。这些协议在不同的网络环境中扮演着关键角色,确保数据在不同媒体上的可靠传输。
数据链路层和物理层:以太网涵盖了数据链路层和物理层的内容。数据链路层规定了数据帧的格式,包括帧头和帧尾,以及MAC地址等。物理层规定了实际的传输介质和信号处理,如双绞线、光纤等。
网线要求:以太网的标准规定了使用双绞线作为主要的传输介质。不同的以太网标准(如10BASE-T、1000BASE-T)可以使用不同类别的双绞线,如Cat5、Cat6等。
传输速率:以太网支持多种传输速率,包括10 Mbps(10BASE-T)、100 Mbps(100BASE-TX)、1 Gbps(1000BASE-T)等。传输速率取决于以太网标准和使用的硬件。
局域网技术:以太网是当前应用最广泛的局域网技术。它在各种场景中得到广泛使用,包括家庭网络、企业内部网络、数据中心等。
与其他局域网技术的比较:令牌环网(Token Ring)是另一种局域网技术,它使用了不同的访问控制方法。无线局域网(Wireless LAN,WLAN)则使用了无线传输介质。每种技术都有其适用的场景和特点。
总之,以太网作为一种标准化的局域网技术,提供了可靠的局域网连接,同时适应了不同的传输介质和速率需求,因此被广泛应用于现代网络中。
以太网的帧格式如下所示:帧头+载荷(IP数据报)+帧尾。
前导码(Preamble):一个7字节的前导码,用于同步接收方的时钟。
帧起始标志(Start Frame Delimiter):一个1字节的帧起始标志,指示帧的开始。
目的地址(Destination Address):6字节,表示帧的接收目标的MAC地址。
源地址(Source Address):6字节,表示帧的发送方的MAC地址。
类型/长度字段(Type/Length):2字节,它可以表示两种不同的内容,描述载荷数据是啥样的。如果值大于等于0x0600(十进制1536),则它表示数据帧的长度,带有一个IP数据报(最大长度不能超过1500字节,我们把数据链路层的数据报能携带的最大载荷长度叫做MTU。IP数据报的分包组包大概率是因为MTU引起的,而不是触发64KB上限引起的,不同的数据链路层的协议的MTU是不一样的,和物理介质有关);如果值小于0x0600,则它表示帧中包含的协议类型(如IP、ARP等)。
数据(Data):46-1500字节的数据字段,包含上层协议的数据。
帧校验序列(Frame Check Sequence,FCS):4字节的CRC校验码,用于检测数据传输过程中是否出现错误。
以太网帧的源地址和目的地址是MAC地址,用于标识设备的硬件地址,而类型/长度字段可表示数据帧的长度或包含的上层协议类型。帧的校验序列(CRC)用于校验帧在传输中是否损坏。帧格式的详细规范可以根据不同的以太网标准而有所不同,但通常遵循上述结构。
MAC地址(Media Access Control Address),也称为硬件地址、物理地址或以太网地址,是数据链路层(数据链路控制子层)的一个重要组成部分。每个网络接口卡(NIC)或网络适配器都具有唯一的MAC地址,它通常是一个由12个十六进制数字(0-9和A-F)组成的字符串,如:00:1A:2B:3C:4D:5E。
MAC地址的作用:
唯一标识设备:MAC地址是用于唯一标识网络中的设备的。没有两个设备应该具有相同的MAC地址。这确保了每个设备在网络中具有唯一的身份。
数据链路层寻址:MAC地址用于数据链路层的寻址,以确定数据包应该被发送到哪个设备。当数据包离开发送设备时,它包含了目标设备的MAC地址。路由器和交换机使用这个地址来正确传递数据包。
无需路由器的本地通信:在本地网络中,当两台设备希望互相通信时,它们可以使用MAC地址,而不需要通过路由器。这使得本地通信更快速和高效。
MAC地址的目的:
总之,MAC地址是在数据链路层上使用的地址,用于设备之间的本地通信,以唯一标识和寻址每个设备,以确保数据包被正确传送到目标设备。在互联网中,MAC地址通常在局域网络中使用,而在全球互联网上,IP地址通常用于寻址和路由数据包。
MAC地址其实和IP地址有一定的重合和冲突,原因还得来看历史背景。由于最开始搞网络的时候,网络层协议和数据链路层协议是各自独立的,各自都发明了地址。
IP地址的用途:
MAC地址的用途:
当考虑IP协议和以太网时,可以将它们视为网络通信的不同层面:
IP协议立足于全局,完成真个通信过程的路径规划工作,源IP和目的IP始终是整个通信过程中的最初起点和终点(不考虑NAT的情况下);以太网则是关注与局部,相邻两个设备之间的通信过程。 源MAC和目的MAC一直在根据你当前转发的过程随之改变。每次到达一个节点,往下一个节点转发的时候,都会改变。
MAC地址和IP地址的区别:
不同的网络层次:
唯一性:
用途:
虽然MAC地址和IP地址在某些方面存在重叠,但它们的不同层次和用途使它们在网络中具有不同的作用和意义。它们通常协同工作,确保数据包在局域网络和全球互联网中能够安全传输。
MTU(Maximum Transmission Unit)是数据链路层中的一个重要概念,它表示在特定网络或数据链路中,能够传输的数据包的最大尺寸。MTU限制了数据包的大小,超过MTU的数据包需要进行分片(分成更小的片段)以适应网络的要求。
MTU相当于发快递时对包裹尺寸的限制。这个限制是不同的数据链路对应的物理层,产生的限制。
一些关键认识和信息包括:
MTU的差异:不同类型的网络和数据链路标准通常具有不同的MTU值。以太网通常具有最大MTU为1500字节,而其他网络类型如PPP(Point-to-Point Protocol)可能具有更小的MTU。
分片:如果一个数据包的大小超过了特定网络或数据链路的MTU,就需要将其分片成更小的片段,以便在网络上传输。分片后的片段将在目标端重新组装成完整的数据包。
重组:接收端负责将分片的数据包重新组装成原始的数据包,以便上层协议进行处理。
填充位:有时,为了满足MTU的要求,需要在数据包中添加填充位,使其达到最小允许的大小,例如以太网帧中的填充位。
路径MTU:在网络中,不同的链路和路由器可能具有不同的MTU。路径MTU是指从源到目标的整个通信路径上的最小MTU值,这对于避免分片和提高性能非常重要。
理解和管理MTU对于网络通信非常重要,因为它涉及到数据包的大小、分片和重组,对网络性能和可靠性产生影响。根据不同的网络环境和需求,网络管理员和应用程序开发人员需要合理地配置和处理MTU。
MTU(Maximum Transmission Unit)是数据链路层中的一个重要概念,它指定了能够在特定网络或数据链路中传输的数据包的最大尺寸。MTU的限制对于不同层级的协议产生不同的影响,包括IP、UDP和TCP协议。
MTU的限制在IP协议层面会导致对较大的IP数据包进行分片。这意味着较大的IP数据包将被分成多个小包,每个小包将带有相同的IP协议头标识(id)。此外,每个小包的IP协议头的3位标志字段中,第2位表示允许分片,第3位表示结束标记,指示是否是最后一个小包(是则置为1,否则置为0)。
在接收端,这些小包将按顺序重组并拼装成原始数据包,然后传递给传输层协议。然而,一旦这些小包中的任何一个丢失,接收端的重组过程将失败。需要注意的是,IP层不会负责重新传输数据,这将成为上层协议(例如TCP)的责任。
对于UDP协议,如果UDP携带的数据超过了某个特定阈值,通常为1472字节(1500字节以太网MTU减去20字节IP头部和8字节UDP头部),则UDP数据包将在网络层分成多个IP数据报。这就意味着,即使只有一个小片段丢失,整个数据包的接收端网络层重组也会失败。因此,MTU对UDP协议具有重要影响,尤其是当UDP数据包需要在网络层分片时,数据包丢失的概率显著增加。
在TCP协议中,一个数据报的大小同样受到MTU的限制。TCP的单个数据报的最大消息长度称为MSS(Max Segment Size)。在建立TCP连接的过程中,通信的双方会进行MSS协商,以确定在数据链路上发送的数据包的最大大小。
通常,MSS的值应该是数据链路层的MTU减去IP头部和TCP头部的大小。在理想情况下,MSS的值应该是在IP层不会被分片的最大长度。双方在建立连接时会在TCP头部的选项中写入各自支持的MSS值,并选择较小的MSS值作为最终的MSS。
总之,MTU对于IP、UDP和TCP协议的影响是不可忽视的,尤其在网络通信中需要考虑数据包的大小、分片和重组,以确保数据的可靠传输和网络性能的优化。对于不同的网络环境和需求,网络管理员和应用程序开发人员需要合理配置和处理MTU,以满足特定网络和数据链路的要求。
ARP(Address Resolution Protocol)协议是一种工作在数据链路层和网络层之间的协议,其主要作用是建立主机的IP地址与MAC地址的映射关系。ARP在网络通信中起着关键作用,因为它帮助确定了如何将网络层的IP地址映射到数据链路层的MAC地址。
ARP和RARP这两个协议不是传输“业务数据”,而是辅助转发的协议。
像交换机这样的设备,收到以太网数据帧的时候就需要进行转发。这个转发过程就需要根据MAC地址判定出数据要走哪个网口。
这里的网口是物理意义上插网线的口(IP协议、路由器,走哪个网络接口其实是抽象的概念,最终还是要在数据链路层才能决定走呢个网口)。
具体如何转发?
当数据帧到达交换机时,交换机会查看数据帧中的目标MAC地址以确定数据帧应该被发送到局域网中的哪个物理端口(网口)。交换机内部使用一种称为"转发表"的数据结构来进行这个决策。这个转发表类似于哈希表,其中包含已知的MAC地址以及与每个MAC地址相关联的物理端口信息。
转发表的作用:转发表是交换机用来了解哪个MAC地址连接到哪个网口的工具。当数据帧到达时,交换机会查询目标MAC地址,并在转发表中查找相应的记录,以确定应该通过哪个网口发送数据帧。
构造转发表:初始时,转发表是空的,交换机不知道任何设备的位置。为了构建这个表,交换机使用ARP协议。ARP协议(Address Resolution Protocol)允许设备在已知IP地址的情况下查找对应的MAC地址。当交换机需要发送数据帧到一个新的设备时,它会广播一个ARP请求到局域网内,询问谁拥有特定IP地址的MAC地址。设备拥有该IP地址的设备将会响应ARP请求,将其MAC地址提供给交换机。
更新转发表:一旦交换机收到设备的响应并获得了目标MAC地址,它会将这个信息添加到转发表中,以便将来的通信。这样,交换机就可以更快速地决定将数据帧发送到正确的设备,而无需广播数据帧到整个网络。
详细过程如下,可简单了解:
接收数据帧:交换机首先接收到一个以太网数据帧。这个数据帧包含源MAC地址和目标MAC地址。
查找转发表:交换机内部维护一个转发表,类似于哈希表,其中记录了已知的MAC地址与其关联的物理端口。当交换机接收到数据帧时,它查找源MAC地址和目标MAC地址。
决定转发:根据数据帧中的目标MAC地址,交换机决定将数据帧发送到转发表中与该目标MAC地址关联的物理端口。
构造转发表:如果交换机在转发表中找不到目标MAC地址,它将在局域网内广播ARP请求。ARP请求会询问局域网内是否有设备拥有目标IP地址对应的MAC地址。
ARP协议:其他设备会收到ARP请求,并如果其中有一个设备拥有请求中指定的IP地址,它会响应ARP请求并提供自己的MAC地址。
更新转发表:一旦交换机获得了目标MAC地址的响应,它将更新转发表,将该目标MAC地址与响应的物理端口关联起来。这样,在未来的通信中,交换机可以直接将数据帧转发到正确的物理端口。
ARP协议的主要作用可以总结如下:
建立IP地址和MAC地址的映射关系:在网络通信中,源主机的应用程序通常知道目的主机的IP地址和端口号,但不知道目的主机的硬件地址(MAC地址)。ARP协议帮助源主机获取目的主机的MAC地址,从而使数据包能够正确发送到目的主机。
硬件地址验证:当数据包到达主机的网络接口时,主机的网卡会首先接收数据包,然后再传递给上层协议栈。如果接收到的数据包的硬件地址与主机网卡的硬件地址不匹配,主机会直接丢弃这个数据包,因为它不是发给自己的。
ARP协议的工作流程包括以下步骤:
ARP请求:源主机想要发送数据给目的主机,但只知道目的主机的IP地址,不知道其MAC地址。因此,源主机发出一个ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少?”。这个ARP请求广播到本地网络段,以太网帧首部的硬件地址字段填充为FF:FF:FF:FF:FF:FF,表示广播。
ARP响应:目的主机接收到广播的ARP请求,发现其中的IP地址与自己的IP地址相符。目的主机随即向源主机发送一个ARP应答数据包,将自己的MAC地址填写在应答包中。
ARP缓存表:每台主机都会维护一个ARP缓存表,其中存储了已知的IP地址和对应的MAC地址。这些表项通常具有过期时间,例如20分钟。如果某个表项在20分钟内没有再次使用,它将失效,然后下次需要再次发起ARP请求来获取目的主机的硬件地址。
ARP协议在网络通信中扮演着关键的角色,帮助确定了如何将IP地址映射到MAC地址,以确保数据包正确传递到目的主机。它是网络通信的基础之一,因此了解ARP协议的工作原理对于理解网络通信过程非常重要。ARP缓存表的管理和维护也是网络管理员需要关注的方面,以确保网络正常运行。