学习TCP协议时一直说是TCP把自己发送缓冲区的数据发送到对方的接收缓冲区,TCP协议所谓的”发送",是什么意思?TCP是真的发送了吗?
一个报文真正被推送到网络且能被网络设备识别的要么是MAC帧,要么是IP报文和它的有效载荷。网络的其他设备一般不关心TCP的报头,只有收发双端会关心。
所以TCP协议并没有真正发送,它所谓的”发送"就是把数据向下交付,交给IP层来处理。
我们所谓的IP, 即IP地址,IP能干什么呢?
我们现在来举一个例子:
ip + port :把数据从A主机上的进程跨网络送到B主机上的进程
假设我们现在去旅游,要去的目的地是北京的天安门广场。我们肯定是分2步到达:
IP地址也是这样的,IP地址 = 目标网络 + 目标主机。
主机: 配有IP地址的设备; 路由器: 即配有IP地址, 又能进行路由控制; 节点: 主机和路由器的统称;
4位版本号(version):指定IP协议的版本, 对于IPv4来说, 就是4(即4字节32bit);IPv4与IPv6并不兼容,IPv6是用128bit来表示IP地址
4位头部长度(header length):基本单位是4字节,表示IP头部的长度是多少个32bit, 也就是 length * 4 的字节数。4bit表示最大的数字是15, 因此IP头部最大长度是60字节。
8位服务类型(Type Of Service): 3位优先权字段(已经弃用), 4位TOS字段, 和1位保留字段(必须置为0)。4位TOS分别表示:最小延时, 最大吞吐量, 最高可靠性, 最小成本。这四者相互冲突, 只能选择一个。对于ssh/telnet这样的应用程序, 最小延时比较重要; 对于ftp这样的程序, 最大吞吐量比较重要。
16位总长度(total length):IP数据报整体占多少个字节。
16位标识(id):唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了, 那么每一个片里面的这个id都是相同的。
3位标志字段:第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到)。第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文。第三位表示"更多分片", 如果分片了的话, 最后一个分片置为0, 其他是1。类似于一个结束标记。
13位分片偏移(framegament offset):是分片相对于原始IP报文开始处的偏移。其实就是在表示当前分片在原报文中处在哪个位置。实际偏移的字节数是这个值 * 8 得到的。因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了)。
8位生存时间(Time To Live, TTL):该报文转发过程中最多经历路由器的次数(即数据报到达目的地的最大报文跳数)。一般是64。每次经过一个路由,TTL-= 1
, 一直减到0还没到达, 那么就丢弃了。这个字段主要是用来防止出现路由循环
8位协议:表示上层协议的类型,即有效载荷是什么报文
16位首部校验和:使用CRC进行校验, 来鉴别头部是否损坏。
32位源地址和32位目标地址:表示发送端和接收端。(这就是为什么在套接字部分我们把点分10进制字符串风格ip地址转化成4字节ip地址,由于协议需要)
选项字段(不定长, 最多40字节):略
根据8位协议,表示自己有效载荷是什么报文来向上交付
现在我们假设一种场景:故事发生在学校。
前提:学校会为每个学生编号,由学校管理者顶层设计出来的学号是有规律的,用来定位学生的唯一性。
我叫张三,今天突然在操场上捡到一个钱包,包里有32块钱和银行卡若干,由于前几天下雨的原因,钱包中学生卡上的姓名等信息已经模糊了,只留下一串学号。我要把钱包还给失主,于是我在学校每碰到一个人就让他给我背学号与学生卡上的学号对比看看是否相等,这样查找的方式可能失主本人已经毕业了我还没找到。用背学号的方式 ,是遍历我们学校所有的学生,时间复杂度:O(n),每次查找排除一次只能排除一人,效率低下。
下面我们以学院为单位,不考虑专业和班级。我想到学校还有学生会,每个学院都有自己的群,群主是院学生会主席,又给院学生学生会主席拉群形成学生会主席群,同时每个学院都有自己的编号,一个学生的学号是由:院号 + 院内序号 构成的。 我的学号是:10299,钱包中学号是:15003,虽然我不知道她是谁,但我知道她不是我们学院的学生。我把钱包中学号信息拍照放到计算机学院的群里。此时院学生会主席虽然不知道15003是哪个学生,但是他知道这名学生隶属于电工院的,他直接把消息转到学生会主席群并@电工院主席,电工院主席看到学院编号是15,确定是自己院的,于是把消息转到电工院群里@003号同学,让他确认钱包是自己的后联系张三,003同学看到后联系张三拿回自己的钱包。
上面以学生会的方式归还钱包的效率明显提高,为什么?不是以个人为查找单位的,是以群为单位。查找的本质是排除的过程,选择一个群,就是排除了其他所有的群,查找效率大大提高。
这里的群:称为子网;院学生会主席:路由器;学生会主席群:转发集群,公网;群里的各个人:主机;学号:IP地址
查找钱包的过程:
先找到到目标群(子网)
再找到目标人(目标主机)
为什么要拉群:提高查找效率
为了能够让我们将数据从A跨网络更高效地送到B,所以我们必须进行子网划分。
IP地址 = 目标网络 + 目标主机。IP地址的构成是子网划分的结果,提高全球中任何一台主机查找另一台主机的效率
那我们现在就有疑问了,子网划分是谁做的
运营商,国内的网络一定是被国内的运营商顶层设计过的!国际,大家商量着来。
IP地址分为两个部分, 网络号和主机号
通过合理设置主机号和网络号, 就可以保证在相互连接的网络中, 每台主机的IP地址都不相同。
那么问题来了, 手动管理子网内的IP, 是一个相当麻烦的事情。
补充:
上图路由器级联了2个子网,必须配2个IP地址。
路由器代表子网的出口或入口,一般都是整个子网的1号主机。当你进行入网时,路由器一定是子网中的第一个设备。(就像家里的无线路由器)
过去曾经提出一种划分网络号和主机号的方案, 把所有IP 地址分为五类, 如下图所示
随着Internet的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请B类网络地址, 导致B类地址很快就分配完了, 而A类却浪费了大量地址;
针对这种情况提出了新的划分方案, 称为CIDR(Classless Interdomain Routing):
示例1:
IP地址中的主机地址全部设为0, 代表网络号;将IP地址中的主机地址全部设为1, 代表广播地址。所以不用全0和全1表示的主机号,即0~255一共256个IP地址,接入的主机数一共256-2=254个
示例2:
说明:下面子网划分的过程是以国家,省,市为单位的,实际子网划分并非如此而是以地区,人口密度等划分的
一位美国人IP地址1.2.3.4想要访问2.4.4.23的网站,国内出口路由器发现不是本国内的,于是转发到公网上,公网路由器识别出哪个国家后转发到本国的入口路由器处,后将2.4.4.23与子网掩码按位与得到网络号,由本国内的路由器识别后继续向下转发最后访问到了西安某地区的网站。
上面我们是以公网的形式划分子网,并未画全同时发现划分到某市的某个区再往下去细分,这种方式很快把IP地址分配完了。所以实际中并不是这样的,到了一定区域内会构建局域网使用私有IP(后面讲)
127.*
的IP地址用于本机环回(loop back)测试,通常是127.0.0.1
我们知道, IP地址(IPv4)是一个4字节32位的正整数。那么一共只有2的32次方个IP地址, 大概是43亿左右。而TCP/IP协议规定, 每个主机都需要有一个IP地址。
这意味着, 一共只有43亿台主机能接入网络么?
实际上, 由于一些特殊的IP地址的存在, 数量远不足43亿; 另外IP地址并非是按照主机台数来配置的, 而是每一个网卡都需要配置一个或多个IP地址。
CIDR在一定程度上缓解了IP地址不够用的问题(提高了利用率, 减少了浪费, 但是IP地址的绝对上限并没有增加), 仍然不是很够用。 这时候有三种方式来解决:
如果一个组织内部组建局域网,IP地址只用于局域网内的通信,而不直接连到Internet上,理论上 使用任意的IP地址都可以,但是RFC 1918规定了用于组建局域网的私有IP地址。
10.*
, 前8位是网络号,共16,777,216个地址172.16.
到172.31.
,前12位是网络号,共1,048,576个地址192.168.
,前16位是网络号,共65,536个地址源IP在内网中不断被替换的过程称为NAT技术,由此我们可以在LAN口IP处配置IPV6,WAN口IP处配置IPV4,很容易做到在内网环境中使用IPV6。
大家也可以ifconfig
查看自己的云服务器IP
时间线拉回20年前,我考上了上海的同济大学。现在开学了,我要坐火车去上学,刚下火车发现自己的行李不见了,也没有手机,只有兜里的20块钱,想着到了大学就能寻求辅导员的帮助,但是此时我并不知道同济大学在哪里。于是我出了火车站开始找人问路,在路边看见了一个大爷询问怎么去同济大学,他告诉我沿着这条路走200米到公交站坐308公交车一下车能到上海的东北角(假设同济大学就在上海的东北角);我下公交车又不知道咋走了于是又找了一个大爷问路,大爷让我去找左手边50米处的大妈寻求帮助,大妈在这里扫地20年了,她对周围方圆50里很熟悉;大妈告诉我下一站去哪,我坐上公交车去,下了车又不认识路了,看到门口的大爷后找他问路,他缓缓地走回门房掏出一块牌子上面写着:同济大学,告诉我他是同济大学的保安,我已经到了。
上面的故事里:
路由的过程, 就是这样一跳一跳(Hop by Hop) “问路” 的过程。
所谓 “一跳” 就是数据链路层中的一个区间。具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间。
IP数据包的传输过程也和问路一样。
那么如何判定当前这个数据包该发送到哪里呢? 这个就依靠每个节点内部维护一个路由表;
假设某主机上的网络接口配置和路由表如下:
这台主机有两个网络接口,一个网络接口连到192.168.10.0/24网络,另一个网络接口连到192.168.56.0/24网络;
路由表的Destination是目的网络地址, Genmask是子网掩码, Gateway是下一跳地址,Iface是发送接口,Flags中的U标志表示此条目有效(可以禁用某些条目), G标志表示此条目的下一跳地址是某个路由器的地址,没有G标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发;
转发过程例1: 如果要发送的数据包的目的地址是192.168.56.3
转发过程例2: 如果要发送的数据包的目的地址是202.10.1.2
依次和路由表前几项进行对比, 发现都不匹配;
按缺省路由条目, 从eth0接口发出去, 发往192.168.10.1路由器;
由192.168.10.1路由器根据它的路由表决定下一跳地址;
最大传输单元(MTU)
数据链路层的MAC帧协议规定了自己的有效载荷不能超过超过某个值,这个值就叫做最大传输单元(Maximum Transmission Unit,MTU),这个值的大小一般是1500字节。这1500个字节限制包括了IP报头+IP报文中的有效载荷。所以一旦IP发送数据超过1500字节时,就需要先在IP层对该数据进行分片,然后再将分片后的数据交给下层
在Linux下使用ifconfig
命令可以查看对应的MTU。
16位标识(id):唯一的标识主机发送的报文。如果IP报文在数据链路层被分片了, 那么每一个片里面的这个id都是相同的。
3位标志:第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到)。第二位置为1表示禁止分片,这时候如果报文长度超过MTU, IP模块就会丢弃报文。第三位表示"更多分片", 如果分片了的话, 最后一个分片置为0, 其他是1。类似于一个结束标记。
13位分片偏移(framegament offset):是分片相对于原始IP报文开始处的偏移。其实就是在表示当前分片在原报文中处在哪个位置。实际偏移的字节数是这个值 * 8 得到的。因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了)。
怎么做的分片和组装呢?根据上面3个报头字段,以一个长度为4500的报文为例
上面就是分片的过程,组装时只需将报文按照13位片偏移升序排序后线性遍历即可。
原理解析
同一时间段内,接收方可能收到了大量的IP报文分片,它怎么分辨这些报文分片属于哪个客户端呢?
接收方收到的IP报文中,IP报头中携带了32位源IP地址,根据源IP地址就具有了区分不同客户端的能力
你怎么知道分片了呢?
报文类型 | 更多分片 | 片偏移 |
---|---|---|
开始报文 | 1 | 0 |
中间报文 | 1 | 大于0 |
结束报文 | 0 | 大于0 |
独立报文 | 0 | 0 |
怎么保证你把分片全收到了呢
怎么保证把分片组装在一起呢?
按照13位片偏移量对报文进行排序
怎么保证组合在一起的分片一定是对的?
首部:IP报头中存在16位首部校验和,一旦IP报头出现问题会将此报文丢弃;
内容:TCP报头中存在16位校验和,一旦检测TCP自己的报文(即IP的有效载荷)内容有问题会将此报文丢弃
在网络中,分片尽量让它成为少数情况
为什么要避免分片?
即使传输层并不关心IP层的分片问题,但是分片也会对传输层产生影响。
怎么做才能避免分片?
让传输层向下交付的数据尽量不要过大
TCP作为传输控制协议,它需要控制一次向下交付数据不能超过某一阈值,这个阈值就叫做MSS(Maximum Segment Size,最大报文段长度)。这就是为什么TCP滑动窗口中,为什么不把滑动窗口里面的范围报文之间到一个包整体发出,而要分成多个报文发出的原因。
MAC帧的有效载荷最大为MTU,MTU的大小一般是1500字节。IP标准报头20字节,则IP有效载荷=1500 - 20 = 1480。IP有效载荷=TCP报文,TCP标准报头20字节,TCP有效载荷 = 1480 - 20 =1460。因此MSS的值一般就是1460字节。
分片后,每一片都是IP报文,要给没有报头的IP报文添加报头。
超时重传。因此分片会增加传输层重传数据的概率。
怎么做才能避免分片?
让传输层向下交付的数据尽量不要过大
TCP作为传输控制协议,它需要控制一次向下交付数据不能超过某一阈值,这个阈值就叫做MSS(Maximum Segment Size,最大报文段长度)。这就是为什么TCP滑动窗口中,为什么不把滑动窗口里面的范围报文之间到一个包整体发出,而要分成多个报文发出的原因。
MAC帧的有效载荷最大为MTU,MTU的大小一般是1500字节。IP标准报头20字节,则IP有效载荷=1500 - 20 = 1480。IP有效载荷=TCP报文,TCP标准报头20字节,TCP有效载荷 = 1480 - 20 =1460。因此MSS的值一般就是1460字节。
分片后,每一片都是IP报文,要给没有报头的IP报文添加报头。