就目前而言,多数网络编程的系列的文章都在围绕着计算机网络体系进行阐述,但其中太多理论概念,对于大部分开发者而言,用途甚微。因此,在本系列中则会以实际开发者的工作为核心,从Java程序员的角度出发,详细解读Java的网络编程核心内容。
先来看看维基百科对于 计算机网络 这个名词的解释:
计算机网络是指将位于不同地理位置,但具有独立功能的多台设备,通过通信设备和线路连接起来,在网络操作系统,网络管理软件、网络通信协议的协调管理下,实现资源共享和信息传递的计算机系统。
简单来说,计算机网络就是用于满足不同机器之间通信、共享的一种系统。
其实一开始计算机网络是用于军事化目的的,因美国军方需要一种多节点的通信技术,确保在其一部分节点被摧毁后,也能够确保通信依旧正常,因此组建了一个部门 ARPA
并在1966年完成了 ARPANET
(阿帕网)项目,该项目也是最早的计算机网络之一。
当然,后续随着科技不断进步,各种网络相关的技术百家争鸣,出现了各式各样的网络技术,但同时也带来了很大的局限性,例如:不同的计算机网络之间并不能相互通信、不同操作系统之间无法通信、覆盖范围及其有限等。因此, ARPA
需要一种技术将不同的计算机局域网互联,最终 ARPA
组织创建了一个新的项目被称为 internetwork
因特网,也被称为“互联网”。
在 internetwork
项目的研发过程中,1974年, Robert E. Kahn
以及 Vinton G. Cerf
两位教授正式提出了新的传输协议: TCP/IP
协议,用于满足不同计算机网络之间的互联通信, ARPA
到 1982
年接受 TCP/IP
选定为 Internet
主要的计算机通信系统,作为因特网通信的“基石”。
从组成上而言,计算机网络主要由主机、协议、传输介质以及软件四部分组成。
TCP/IP、IPX/SPX、AppleTalk
计算机网络体系主要提供的两个核心功能就在于:
资源子网以及通信子网也对应着 OSI
中的上三层和下三层。
不过如果学习过计算机网络课程的小伙伴应该都知道,其实计算机网络是一个复杂而庞大的系统,由软件、驱动、硬件、线路等多部门共同组成,但对于这些理论性内容不再阐述,有兴趣的可以自行了解。
计算机网络是个非常复杂的系统,由于最初各个计算机网络体系结构不同,导致双方网络之间无法互通,因此,20世纪90年代, ISO
国际标准化推出了 OSI
模型打算制定计算机网络体系标准。
简单来说就类似于国内的普通话,因为不同省份的方言不同,导致不同地方的人交流起来并不方便,因此定义了“普通话”这一标准,所有人都遵循该标准进行交流沟通。
但由 ISO
组织制定的 OSI
网络七层模型结构并没有得到广泛应用,实际中应用最广泛的是 TCP/IP
体系结构。换句话说, OSI
七层模型只是理论上官方制定的国际标准,而 TCP/IP
体系结构才是事实上的国际标准。
PS:国际标准化组织提出的 OSI
模型未得到广泛应用的原因:
①制定时太过理想化,未考虑实际的应用场景。
②拆分过于精细化,实现起来过于复杂,运行效率并不高。
③ OSI
体系的制定周期过长,导致按其标准生产的设备无法及时投入市场抢占份额。
④ OSI
体系因为分层过多,设计也不合理,有些相同的功能在多个层次重复出现。
但因为 TCP/IP
体系中的最后一层没有制定规范的标准,所以对于学习计算机网络完善体系时会缺失一部分,所以又提出了一种折中方案,也就是综合 OSI
以及 TCP/IP
两个体系的优缺点,提出了一种五层结构的原理体系,因此在接触计算机网络体系时,通常都会存在三种分层结构:
OSI TCP/IP
计算机网络体系采用的是分层结构,每个层次之间互不干扰,上层不需要关心下层的具体实现,下层只需要为上层提供服务即可。
在 ISO
组织定义的国际标准 OSI
模型中,是七层网络模型。
在实际应用最广泛的 TCP/IP
模型中,是四层网络模型。
当然,在计算机网络教学中,也提出了五层网络模型。
但无论多少层网络模型,其实本质上都是在叙述相同的内容,只不过拆分的精细度不同而已,三种网络模型对应如下:
OSI七层网络模型中,每层的功能如下:
在 TCP/IP
体系中,将原本 OSI
体系中的应用层、表示层、会话层合并为了应用层,将数据链路层和物理层合并为了数据链路层,因此 TCP/IP
体系中只存在四层结构。
注意点:在最初因为技术问题,因此数据传输必须建立在物理实体介质的基础之上,但如今无线电的电磁波技术也可以做到利用空气作为介质,从而达到传输数据的目的。
在 OSI
分层体系中,上三层:应用层、表示层、会话层组成资源子网,下三层:网络层、数据链路层、物理层组成通信子网。
2.1.1、OSI模型的通信的封装与解析过程
在网络通信中,通信双方都必须是对等的,并且双方都遵守相同协议的,这样才可通信。好比举个例子:
竹子想发微信信息给熊猫,喊熊猫出来玩,但双方之间的通信基础必须对等,也就是说,熊猫的手机上也必须要安装了微信,否则竹子无法通过微信与熊猫出来玩。
但如果竹子说的是汉语,熊猫只能懂汉语,双方并未遵守相同的“协议”,那两者之间自然也无法进行通信。因此,通信的另一要素则是:通信双方都必须遵守相同的协议。
而在计算机网络中,当两台机器进行通信时,其过程主要包含数据的封装与解封:
如上图,左侧是发送端,右侧是接收端,当发送端的应用尝试传输一条数据时,数据会根据网络分层结构,从上至下依次封装,每层都对数据做一定的处理,最后在物理层转换为比特流(二进制数据),然后传输至接收端。
因为双方之间遵守的都是相同的协议体系(网络模型),因此接收端会首先接收到比特流数据,然后从下至上依次解封数据,最终在应用层还原数据,从而达到通信的目的。
TCP/IP
应该是计算机网络中听的次数最多的词汇,但它并非单纯的指 TCP
和 IP
两个协议,而是一组协议的代名词,指由许许多多的网络协议一同组成的“协议簇”, TCP/IP
协议簇主要组成如下:
上面列出了一些 TCP/IP
协议簇中常用的一些协议,但整个协议簇是由大大小小上百个协议一同组成的(对于 TCP/IP
协议簇中的一些常用协议可参考:百度百科)。
在计算机网络体系中,每层都会存在多个协议,但一个协议只会隶属于一层。同时,协议也可以由软件、硬件两个维度来实现。
除开最常见的TCP/IP协议簇外,还有一些其他的协议组,如 IPX/SPX、NetBEUI、AppleTalk
等协议组。
2.2.1、通信协议的基本概念
前面的叙述中,更偏向于“空中楼阁”般的理论概念,比较生涩难懂,接下来我们以实际的 TCP/IP
协议出发,再对计算机网络更进一步阐述。
TCP/IP通信协议是最完整、使用最广泛的网络协议体系,它的魅力在于可使不同硬件结构,不同操作系统的计算机相互通信,通信协议中定义多方之间发送/接收的报文格式、顺序以及规则,其中语法定义了传输的报文格式,语义定义了报文传输规则,时序定义了报文传输顺序, 计算机通信的本质就在于信息报文的相互交换 。
2.2.2、协议数据单元(PDU)
计算机网络体系中,不同节点通信时交换的信息报文被称为 协议数据单元(PDU) , PDU
主要由协议头信息+数据( SDU
)组成,数据是指发送端需要传输给接收端的信息,而协议头信息中,则包含了完成传输所需的控制信息,如地址、长度、序号、分段标识、差错控制信息等。
基于 TCP/IP
体系而言, PDU
在不同层存在不同的称呼,如下图:
就目前而言,前面的阐述中已经将计算机网络中最常见的 OSI、TCP/IP
体系简单介绍明白了,但对于这些体系中,不同层的具体作用却并没有进行探讨。因此,接下来我们从自己设计一个计算机网络体系的角度出发,分别来探讨网络分层的必要性与每个分层的具体含义。
2.3.1、物理层
先来看最简单的通信模型:
如上图,两台电脑之间通物理媒体连接,互相传递信息,从而达到通信的目的。
在这种最简单的直连通信模型中,想要实现双方之间的通信,那应该考虑如下几个问题:
0
和 1
。对于如上问题,我们可以将其全部归纳到“物理层”来解决,在该层中定义标准规范。
简单来说,物理层主要任务是 负责在介质上正确的传送比特流 ,并 规定物理接口的各种特性和物理设备的标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率 等。
2.3.2、数据链路层
但一般现在不会存在直连型网络,常用的计算机网络都是由多台机器共同组成的,如下:
在这种总线连接的通信模型中,当一台主机,比如 A
向 D
发送数据时,数据会通过总线传输至总线上的所有主机,此时 D
如何知道该条数据是发给自己的呢?其他主机又如何区分数据不是发给自己的呢?因此,对于这种多台机器组成的网络模型而言,我们也需要解决一系列问题,如:
MAC
对于上述中的这些问题,可以全部划归到链路层处理。
因此可以得出,数据链路层主要任务是 实现数据在不可靠的物理线路上的可靠传递 ,负责 提供物理地址寻址、数据的成帧、流量控制、差错控制等功能,确保数据的可靠传输 。
PS:对于这种总线型网络早已淘汰,目前常用的则是以太网的交换式通信模型:
2.3.3、网络层
到目前为止,解决了物理层及链路层中的问题后,就可以实现数据在一个局域网络中的传输了。但实际每天都接触的因特网是由无数个小网络和路由器互连组成的,如下:
因此只解决前面所叙述的问题还是无法在庞大的计算机网络中实现通信,毕竟之前因为只有一个网络,所以不需要对网络进行标识。但在这种广域网模型中,会存在大大小小无数个局域网,所以想要从一台主机将数据传输给另一台主机时,此时又该如何精准定位到某个网络中的具体主机呢?因此又产生了一系列的问题需要加以解决:
而对于这些问题我们都可以划归到网络层处理。
所以,网络层主要负责 将网络地址翻译成对应的物理地址 ,并 通过路由选择算法为数据的传输选择最适当的路径 。
路由选择:路由主要包括路由表和路由算法两个方面,网络层会根据速度、距离(步跳数)、价格及网络拥塞程度等因素,在路由表的多条通信路径中找一条最佳路径。
2.3.4、传输层
至此,如果解决了物理层、链路层及网络层的问题,数据就可以做到在不同的网络中进行通信。但这对于普通用户而言仍然不够,因为一台主机上会安装多个应用程序,好比:
主机A上安装了QQ、微信、谷歌浏览器,当另一台主机向主机A发送数据时,那这些数据到底应该交给主机上的哪个进程处理呢?
因此还需要解决:
这些问题可以被划归到传输层加以解决。
基于 OSI
模型而言,上三层资源子网主要负责用户数据处理,下三层通信子网主要负责数据通信,而传输层位于OSI模型中的第四层,是资源子网与通信子网之间的桥梁,因此,无论是在 OSI
亦或 TCP/IP
体系中,传输层都是至关重要的一层。
传输层的主要功能是: 提供端到端之间的传输机制以及提供差错效验和流量控制,保证报文的正确传输。 在 TCP/IP
体系中,主要提供了两种传输服务:
TCP UDP
2.3.5、应用层
应用层无论是在 OSI
亦或 TCP/IP
模型中,都是位于最高层,它是用户、各种应用程序和网络之间的接口,其功能是直接向用户提供服务,并完成用户在网络中的各种操作,应用层为用户提供的服务范围涵盖:文件目录服务、文件传输服务、远程登录服务、电子邮箱服务、打印服务、网络管理服务、安全服务、域名管理服务等,这些服务由应用层中各种协议及应用程序提供。
TCP/IP
体系中将资源子网都合并为了应用层,但在 OSI
模型中,资源子网是分为应用层、表示层、会话层三个层次的,也就是拆分粒度更精细化,服务也更为精细化。
2.3.5.1、OSI-表示层
主要负责应用层的用户数据处理,例如数据编/解码、数据的加密解密、数据压缩、数据格式处理等,其作用如其名,主要处理用户信息的表示问题。
2.3.5.2、OSI-会话层
会话层位于OSI模型的第五层,是用户应用程序和网络之间的接点,主要负责网络中的两节点间建立、管理和终止通信。会话层的功能涵盖:建立通信链接、保持会话过程、维持通信链接的畅通、同步节点间的对话信息、决定通信是否终止以及通信中断时决定从何处重新发送。
简单而言,应用层主要是 为用户与网络之间,提供各类可“接触”的窗口。
2.3.6、TCP/IP体系中数据的通信过程
与之前的 OSI
模型的通信过程相同, TCP/IP
体系中,一端想往另一端传输数据时,数据也会经历封装与解封的过程,数据经过每一层时都会加入当前层的协议信息,从而形成本层的 PDU
,继而向下传递,直到最终被转换为比特流数据,通过传输介质转换为信号传输到另一端,另一端接收到之后则会依次解封数据,过程如下:
同比生活,也就是类似收发快递的过程:
到目前为止,计算机网络中一些比较重要的基础概念已介绍完毕,前面的论述中,从计算机网络的诞生,到法定的 OSI
标准体系以及 TCP/IP
体系进行了简单分析,对于开发者而言,这些基础知识了解即可,想要深入学习的小伙伴可观看 《湖科大的计算机网络视频教学》 ,讲的比较透彻切通俗易懂。
前面更多的在叙述计算机网络的整体的结构,接下来则准备对计算机网络中几个常见的网络协议进行阐述,如 IP、TCP、UDP
等协议( HTTP、HTTPS
放在下章讲解)。
当应用层的数据被封装后,想要将数据在网络上传输,数据究竟要被发往何处,又该如何精准的在网络上定位目标机器,此时起到关键作用的就是“ IP
协议”。
IP
协议的作用在于把各种数据包准确无误的传递给目标方,其中两个重要的条件是 IP
地址和 MAC
地址。其中 IP
地址就是所有主机在网络通信中的唯一标识,但由于 IP
地址是稀有资源,不可能每个主机都拥有一个 IP
地址,因此路由器里面会记录我们主机的 MAC
地址,通常的 IP
地址是路由器根据 MAC
地址生成的,而 MAC
地址是全球唯一的。
举例:IP地址就如同是物流线路上的驿站地址,而MAC地址就是具体货架上货物的位置。
之前的 IP
地址采用的 IPv4
格式,目前大部分主机都已向 IPv6
过渡。
3.1.1、IP地址的组成与分类
IP
地址一般由 网络标识( NetID
) 和 主机标识( HostID
) 两部分组成,其中网络标识对应着网络地址,表示其局域网属于互联网中的哪一个网络;主机标识对应着一台机器的主机地址,表示机器属于该局域网络中的哪一台主机。
通常情况下,一个 IP
存在四组数字,每组数字对应着八位二进制数字(一个 IP
地址共计 32Bit
),每组之间分别用 .
隔开,其中不同类型的 IP
地址,表示网络标识和主机标识的数字段也不同,目前的 IP
地址主要可分为 A、B、C、D、E
五大类,如下:
A类IP
A
类地址由 8
位网络标识+ 24
位主机标识组成,也就是之前的四组数字中,第一组表示网络地址,而后三组代表主机地址。网络地址的最高位,即首位必须为 0
,其中 0
和 127
都为保留位,因此 A
类 IP
的网络地址范围为 1~126(1.0.0.0~126.0.0.0)
,也就代表着可用的 A
类网络有 126
个,每个网络中最大可容纳一千多万台主机(2 24 -2)。
PS:为什么 A
类网络的地址范围是 1~126
呢?
因为 A
类 IP
中用 8bit
表示网络标识,首位需要保留为 0
,因此剩下的 7bit
能够表示的最大数字为 127
,末位 127
也需保留,因此 A
类网络地址的取值范围为 1~126
。
示意图如下:
例如 32.44.128.5
这个地址,其中第一段代表网络地址,剩余三段表示网络中的主机地址。
B类IP
B
类地址由 16
位网络标识+ 16
位主机标识组成,因此 IP
中第一段和第二段都为网络标识,其中前两位为保留位 1、0
,因此 B
类 IP
的网络地址取值范围为 128~191(128.0.0.0~191.255.0.0)
,总计可用的 B
类网络数量为 16382
个,每个网络中可容纳 6
万多台主机(2 16 -2)。 B
类 IP
组成示意图如下:
例如 128.123.11.32
这个地址,其中前两段为网络地址,后两段表示网络中的主机地址。
C类IP
C
类地址由 24
位网络标识+ 8
位主机标识组成,也就是说,整个 IP
地址中,前三段都为网络地址,最后一段为主机地址。 C
类地址中,前三位都为保留位,即C类网络的最高位必须为 1、1、0
,因此 C
类网络地址的取值范围为 192-223(192.0.0.0~223.255.255.0)
,粗略计算下来, C
类网络的数量可达 209
万左右,每个网络中允许存在 254
台主机(2 8 -2),组成示意如下:
如 192.0.0.121
这个 IP
,前三段为网络地址,最后一段则为网络中的主机地址。
D类IP
D
类和后续的 E
类属于特殊的 IP
地址, D
类地址被称为广播或组播地址,其最高位必须是 1、1、1、0
,因此取值范围为 224~239(224.0.0.0~239.255.255.255)
,如下:
E类IP
E
类地址则属于保留的地址,为以后接入更多的网络预留的 IP
,其最高位必须为 1、1、1、1
,即 E
类 IP
第一段的取值范围为 240~255(240.0.0.255~239.255.255.254)
:
但 255.255.255.255
这个全 1
的 IP
属于特殊含义的地址,表示当前子网的广播地址,如同全 0
的 IP
: 0.0.0.0
代表本机地址一样,带有特殊的含义。
IP分类小结
因不同类型的网络 IP
规模不同,所以它们也分别应用于不同的场景,如:
A
类 IP
适用于大型网络,由于单个网络中可容纳的主机数非常巨大,因此常被保留给政府机构使用。B
类 IP
适用于中型网络,一般会被分配给公益组织、中大型企业等。C
类 IP
适用于小型网络,这种 IP
适用于所有需要网络的个体和小集体,如网吧、家庭、个人电脑等。D
类 IP
用于组播。E
类 IP
用于保留和实验。3.1.2、子网掩码
子网掩码又被称为网络掩码、地址掩码、子网络遮罩,它的作用主要有两个:
IP IP
但默认的子网掩码值也并非都相同,不同的网络类型存在不同的默认掩码,如:
A
类网络的默认子网掩码: 255.0.0.0
B
类网络的默认子网掩码: 255.255.0.0
C
类网络的默认子网掩码: 255.255.255.0
一个子网掩码决定着一个子网(独立的单个网络)内可容纳的主机数量,计算公式为:可容纳的主机数量=( 2
的 n
次方) -2
。这个 n
可以理解为二进制掩码中 0
的数量,例如:
255.0.0.0
转换为二进制为: 11111111.00000000.00000000.00000000
,后面有 24
个 0
,因此可容纳的主机数量为( 2
的 24
次方) -2
,即 16777214
台主机。255.255.255.0
转换为二进制为: 11111111.11111111.11111111.00000000
,后面存在 8
个 0
,因此 C
类网可容纳主机数量为( 2
的 8
次方) -2
,即 254
台主机。修改子网掩码为局域网扩容
在中型企业中,经常会碰到一个问题,公司使用 C
类网,因此子网内可容纳的主机(电脑)数量为 254
台,但实际公司的电脑可能超出这个数量,如存在 440
台电脑,那此时如何更改路由器的设置可以让 440
台电脑同时上网呢?
,对于第一种方案需要增加经济成本,因此可以暂不考虑。来看看第二种方案,此时将子网掩码修改到多少合适呢?计算过程如下:
440/254≈1.73
,向上取整结果 =2
,也就是容纳 440
台电脑需要 2
个网段。2
的 N
次方≈主机数量,求 N
:
2
的 N
次方= 440
,可算出 N>8
,即 N=9
最合适,因此掩码低位应当存在 9
个 0
;11111111.11111111.11111110.00000000
,转换为十进制: 255.255.254.0
;255.255.254.0
最合适。此时假设路由器的 LAN
口配置是 197.118.0.1
,那根据第一步计算出的网段数量,分配两个子网段, 197.118.0.1~197.118.1.254
即可满足需求,采用这种更改子网掩码的方式,就在不增加路由器的情况下,也能达成了为局域网增加可用 IP
数的需求。
最终可用 IP
范围为: 197.118.0.1/23
,可用 IP
地址共计 510
个。
嗯? xxx/23
是什么东东?其实这个值是子网掩码的简写方式,之前算出来的子网掩码为 255.255.254.0
,转换为二进制,一共存在 23
个 1
,因此可以简写为 xxx/23
。
如何判断两个 IP
处于同一个网段
两个 IP
处于同一网段的前提是:两个 IP
的网络标识必须一致,那如何计算网络地址呢?
网络地址 = IP
地址 “位与” 子网掩码。
如C类网 197.118.0.198、197.118.1.114
两个 IP
,子网掩码为 255.255.254.0
,如何计算网络地址?首先需要将掩码与IP全部都转换为二进制,如下:
197.118.0.198
: 11000101.01110110.00000000.11000110
197.118.1.114
: 11000101.01110110.00000001.01110010
255.255.254.0
: 11111111.11111111.11111110.00000000
首先将 197.118.0.198
网络部分(前三段)的二进制值同掩码进行位与运算,最终可以计算出:
11000101.01110110.00000000
,转换为十进制为 197.118.0
。
紧接着再 197.118.1.114
网络部分的二进制值同子网掩码进行位与运算,计算结果:
11000101.01110110.00000000
,转换为十进制为 197.118.0
。
最终可得到结果: 197.118.0.198、197.118.1.114
的网络地址都为 197.118.0
,代表着这两个 IP
处于同一个网段。
子网掩码小结
子网掩码这块的内容,如果你不是专门做网络相关的工程师,哪适当了解即可,如不理解其内容也无关紧要,作为软件开发者并不需要太过深入研究。
3.1.3、IP协议核心流程
经过上述内容学习后,我们已经对 IP
协议中的 IP
组成、分类、计算等知识已经建立起了基础,接下来在重点看看 IP
协议的核心流程。 IP
协议核心主要包含 IP
寻址和路由控制。
前面曾提及:网络上任何一台主机都会存在自己的 IP
地址,那么当应用层数据被封装后,能够精准定位到目标主机的关键原因就在于 IP
,可以通过 IP
地址在网络中进行寻址,从而让数据到达目的主机。
但在实际的网络通信过程中,可能有时数据发送的链路非常遥远,如你从中国向日本网友发送一条数据,那么由于一些网络抖动、物理介质损坏都会导致数据丢失。但此时又该如何确保数据“安稳”到达目的地呢?
为了防止数据由于介质损坏或网络抖动等原因丢失,因此会在数据的传输链路中加入一些“中转站”,也就是所谓的“路由器”,一方面可以备份数据,查看数据是否丢失,如果丢失会重新发送,另一方面也可以控制数据的转发。当然,这个控制数据转发的过程也被称为“ 路由控制 ”。
正因为路由控制的存在,所以即使网络复杂多变,也能够通过路由器的控制将数据“安稳”送达至目的主机。
但因数据传输过程中,数据从发送方到接收方之间存在的链路是不可预估的,因此数据传输的链路上可能会分布很多路由器。本质上数据在链路中传输,就是一个个路由器相互之间交换数据报的过程,当然,这个过程也被称为“ 跳 ”。
跳:数据包经过一台路由器就是一跳。
好比一个网络中存在四个路由器: A、B、C、D
,如果局域网 A
中的主机要给局域网 D
中的主机发送数据,从理论上来说就会经过 B、C、D
三台路由器,也就是会经过三跳。
哪当数据到达某个路由器后,它如何知道“下一跳”该去往何处呢?此时中转的路由器就会解析收到的数据报,然后从中解析出 IP
数据报,然后查询自身的路由表,从而选择“下一跳”该走的路线,最终不断重复该过程直至数据到达目标主机。这种多次转发数据的过程也被称为“ 多跳路由 ”。
3.1.4、IPv4与IPv6
目前的网络几乎大部分还是基于 IPv4
版本,但同时大部分应用程序也开始支持 IPv6
, IPv6
是“ Internet Protocol Version 6
(互联网协议第6版)”的缩写,是用于替代 IPv4
的下一代 IP
协议,也就是下一代互联网的协议。
IPv6
相较于 IPv4
而言,主要不同点在于:
IPv4
地址采用 32
位长度, IPv6
的地址则采用 128
位长度。IPv6
的路由表会比 IPv4
更小、更精细。IPv6
的组播支持以及对流的支持要强于 IPv4
。IPv6
的安全性更高,使用 IPv6
的用户可对网络层数据进行加密。IPv6
允许协议进行扩充,而 IPv4
不允许。前述的 IP
协议分析都是基于 IPv4
版本而言的,因为目前主流的网络版本还是 IPv4
,但如今也逐步向 IPv6
过渡。
TCP(Transmission Control Protocol)
传输控制协议是面向连接的可靠传输协议,是位于传输层的核心协议之一,在不可靠的互联网络上, IP
协议只提供了简单不可靠的包交换,但网络中不同主机之间经常需要一种可靠的、类似于管道一样的连接、流机制,去稳定传输一些数据,如视频、音频、图片等大文件数据。
因此 TCP
应运而生, TCP
协议是为了在不可靠的互联网络上提供可靠的端到端之间,字节流传输而专门设计的一个传输协议, TCP
中采用字节流传输数据。
3.2.1、TCP协议简介
先来看看 TCP
的报文头结构:
TCP
报文头结构中的各字段释义如下:
TCP
为了保证数据的可靠传输,会对分段数据标注序号,勇于组装和确认数据的正确性。TCP
报文头的长度,因为 TCP
头长度可变性,因此需要在头信息中声明每个头的长度。URG
:表示本次发送的报文数据中是否紧急数据。ACK
:确认信号,当报文中 ACK=1
的时候表示正确或同意。PSH
:表示接收方应该尽快将这个报文交给应用层,为后续数据腾出空间,不要停留在缓冲区。RST
:如果收到 RST=1
的报文,说明与主机的连接出现严重错误(如主机崩溃),必须释放连接,然后重新建立连接。SYN
:建立一个新连接, SYN=1
表示这是一个请求建立连接的报文段。FIN
:断开一个连接, FIN=1
表示通知告知对方本段要关闭连接了。ACK
确认后才能继续发送数据。CRC
算法提供额外的可靠性,用于效验数据正确性。TCP
报文头的范畴内)。当应用层向传输层传递数据时, TCP
会首先对数据流进行分段,将大的数据拆分成一个个的数据报文段,然后会将封装好的数据包传递给网络层的 IP
层。同时,为了防止数据在网络传输中丢包, TCP
也会对每个数据包分配一个序号,当接收方成功收到发送的数据后,会返回一个 ACK
确认,如果发送方在规定的合理时间( RTT
)内未收到接收方的 ACK
,那么对应的数据包会被认定为已丢失,发送方会将该段数据重新传输。
当数据接收完成后, TCP
会用校验和函数来检验数据是否正确,在发送方和接收方都需要计算该值,发送方计算后会将该值放在 TCP
头中携带发送,接收方接收到数据后,也会再次计算该值,再与报文头中的值进行比对,确认数据的正确性。
3.2.2、连接管理机制 - 三次握手与四次挥手
由于 TCP
是基于管道连接式通信的协议,因此在数据传递之前,必须要先建立连接,当数据传输完毕后,也必须要关闭连接。因此,这就引出了面试过程中人尽皆知的问题:“ 为什么 TCP
是三次握手,四次挥手! ”
想要了解清楚这个问题的答案,那么得先了解 TCP
建立与关闭连接的过程。
TCP的三次握手
所谓的 TCP
三次握手,其实是指 TCP
建立连接的过程,因为 TCP
属于可靠性的传输协议,因此在发送数据前必须要先确保发送/接收数据的双方状态正常,因此需要经过“三次握手”的过程,具体如下:
SYN=1,seq=x
,然后进入 syn-send
等待确认连接状态。 SYN=1 seq=x
SYN=1,ACK=1,seq=y,ack=x+1
给客户端。
SYN=1
: TCP
是双全工协议,因此服务端也需向客户端发送 SYN=1
信号。ACK=1
:确认客户端建立连接的请求。seq=y
:表示当前服务端返回给客户端的序号。ack=x+1
:确认客户端序号 x
之前的请求都已收到。SYN=1
后,也需返回 ACK=1,seq=x+1,ack=y+1
的数据包。
ACK=1
:表示确认服务端的连接建立请求。seq=x+1
:当前数据包的请求序号。ack=y+1
:确认服务端序号 y
之前的数据都已成功接收。经过如上三个步骤,客户端和服务端双方之间确认请求后,连接会成功建立,紧接着双方都会处于 estab-lishen
状态,数据可以正常传输。
当然,这个过程略微有些难理解,换个日常生活的例子来快速理解一遍!
TCP的四次挥手
当“三次握手”完成后,客户端和服务端之间会成功建立连接,从此开启双方端到端之间的数据传输,当一方数据传输完成后,会尝试中断连接,因此又会经历“四次挥手”的过程,如下:
FIN=1,seq=u
关闭连接的报文,然后等待服务端响应。 FIN=1 seq=u
ACK=1,seq=v,ack=u+1
。
ACK=1
:确认客户端“关闭连接”的请求。seq=v
:请求序号。ack=u+1
:确认客户端序号 u
及其以前的数据都已成功接收。FIN=1,ACK=1,seq=w,ack=u+1
。
FIN=1
:表示自己也要“关闭连接”了。ACK=1
:确认收到了之前的数据。seq=w
:请求序号。ack=u+1
:确认客户端序号 u
之前的数据都已成功接收。ACK=1,seq=u+1,ack=w+1
。
ACK=1
:确认服务端“关闭连接”的请求。seq=u+1
:请求序号。ack=w+1
:确认服务端序号 w
之前的数据都已成功接收。前面提及过,因为 TCP
是双全工的协议,因此双方都可以主动释放连接,在 TCP
中,当一方数据传输完成后,就会主动关闭连接,也就会经历“挥手”的过程,同样我们也可以举个通俗一些的例子来认识这个过程:
..&*/?..!^...
三次握手是指 TCP
建立连接需要发送三个数据包,主要目的是在于:为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
四次挥手是指 TCP
关闭连接时需要发送四个数据包,主要目的在于:当客户端发送完数据后,给服务端发送“我要关闭连接了”的请求,然后服务端告知客户端收到了“关闭请求”,但此时服务端会继续向客户端传输未发送的数据,客户端也照旧可以接收服务端的数据,直到服务端的数据传输完成后,服务端也会发出“关闭连接”的请求,客户端同意后,最终才会断开连接,从而保证数据正常可靠的交互。
本质上“三次握手与四次挥手”就是在指 TCP
建立与关闭连接的过程,搞明白建立和关闭连接的过程后再来看看最开始的问题:“ 为什么 TCP
是三次握手,四次挥手! ”
为什么TCP是三次握手,四次挥手
因为建立连接“握手”时,当服务端接收到“客户端想与服务端建立连接”的请求后,可以立马返回“同意+与客户端建立连接”报文,客户端也确认建立连接后,就可以称为“握手完成”。
但关闭连接时的“挥手”,因为一方数据传输完成后就会提出关闭连接,不过另外一方可能还依旧存在数据未发送完成,因此服务端就不能在“确认关闭”连接的时候,也同时发出“关闭连接”的请求,因为自己的数据还没发送完成呢,所以会等到自身的数据全部传输后,再主动向客户端发起一次“关闭连接”的请求,等待客户端“确认关闭”后,从而完成整个“挥手”动作。
3.2.3、TCP中的沾包问题
要理解“TCP沾包”问题之前,首先要理解 TCP
传输数据的方式。 TCP
在传输数据时,会给每个分割后的报文段分配一个序号,接收方在收到数据后,会按照序号排好,然后将其放置在 TCP
缓冲区中。同时 TCP
为了提升传输速度,若连续几次发送的数据都很少, TCP
会根据优化算法把多个数据合并成一个包发出。
沾包问题:多个数据包在一块儿,无法确定每个数据包之间的分割边界,因此从应用层的角度看来,就好像多个数据包“沾”在了一起。
对于 TCP
传输层而言,发送方和接收方都有可能造成数据沾包问题。
发送发导致的数据沾包问题: TCP
为了优化传输速度,往往会等收集到足够多的数据后才发送一包数据,因此发送发传输的数据就出现了沾包问题。
还有一种情况则是:当需要发送的数据大于 MSS
规定,那么 TCP
就会对数据包进行拆包,一个数据包会被分开传输,最终导致数据出现沾包问题。
接收方导致的数据沾包问题: TCP
中,如果数据被接收后,应用程序没有及时读取缓冲区中的数据报文,就会导致缓冲区中堆积大量的报文数据。这种情况下,站在 TCP
层的角度而言,看到的是一个个的数据报文依次排列着,但对于应用层的程序来说,看到的就是一串连续的字节流数据,应用程序无法知道每个数据包之间的分割边界,站在应用层的角度来看,所有的数据包就好像都“沾”在一起了一样,应用程序根据预先设定好的大小从缓冲区中接收数据,最终会一次性读取到多包数据。
沾包问题解决方案
TCP
短连接时,不必考虑沾包问题。3.2.4、TCP协议中的其他核心概念
因为 TCP
既要保证可靠性,同时又要尽可能提高传输性能,所以整个 TCP
设计的尤为复杂,牵扯出的概念也很多,比如为了保证数据的可靠传输, TCP
中提供的机制:
ACK ACK TCP TCP
在确保了数据可靠传输的同时, TCP
也提供了一堆机制尽可能提高数据传输性能:
ACK
这种应答机制大大影响了传输效率,因此 TCP
中引入了一个新概念:窗口,窗口大小是指无需等待 ACK
就可继续发送数据的最大值,当收到第一个 ACK
确认应答后,窗口向后移动,发送后续的数据,因为这个窗口不断向后滑动,所以叫做滑动窗口。ACK
应答序号,当发送方连续三次接收到相同的应答序号时,就会对该序号的数据报进行重发。ACK
应答可以“搭数据的顺风车”,和接收方回应的数据报文一起返回,如三次握手中的第二次握手,采用的就是捎带应答机制, ACK
应答信号和建立连接的数据报一起发送给了客户端。同时, TCP
协议中,每建立一条连接都会维持九个定时器,可以适当了解:
TCP
连接。0
,阻止发送方继续发送数据。当缓冲区数据被读取后,会再向发送方发出一个 ACK
,通知发送方可以继续传输数据,但为了防止该 ACK
丢失导致死锁现象出现,发送方也会启动零窗口探测定时器,每隔一段时间,发送方会主动发送探测包,迫使接收端响应,从而得知其接收窗口有无打开。ACK
延迟定时器:延时应答的定时器,和超市重传定时器功能类似。SYNACK
定时器:新建连接时等待 ACK
应答的定时器,超出时间后会关闭连接。FIN_WAIT2
定时器:关闭连接时等待 ACK
的定时器,超时后会取消“关闭连接”。ER
延迟定时器、 PTO
定时器、 TIME_WAIT
定时器:这三玩意儿没研究过。UDP(User Datagram Protocol)
协议是传输层的一个不可靠传输协议,它为应用程序提供了一种无需建立连接就可以发送封装的 IP
数据包的方法。在传输层中,与 TCP
协议互补, UDP
除了给应用层提供了发送数据包的功能外,几乎没有做任何其他事情。而面向连接的 TCP
恰恰相反,几乎做了所有的事情。
刚刚提到过, UDP
仅为应用层提供了发送数据报的功能,主要就是指 UDP
对 IP
协议的扩充:
IP IP
接下来先看看 UDP
的报文头结构。
3.3.1、UDP的报文头
对比 TCP
复杂的报文头结构, UDP
的头部就显得比较简单了,整个头共 8
字节:
Header+Data
的总长度,因为 UDP
头为 8
字节,所以该值最小为 8
。UDP
检验和并非必须的),就算效验时检测出错误,也仅只是丢弃数据包,不会对数据进行纠正,也就是不会重发数据报。3.3.2、UDP核心流程
UDP
是一个无连接的协议,因此采用 UDP
传输协议的程序,在传递数据时,不会存在建立/释放连接的过程。当数据需要传输时,会对于应用层的数据简单的封装,也就是加上自己的 UDP
头后,直接会将数据丢给 IP
层,然后交由链路传输。
正因为如上特性,因此 UDP
的传输速度仅受到数据生成的速度、计算机算力和传输带宽的限制。
在接收端, UDP
会把每个消息段放在队列/缓冲区中,程序每次从队列中读一个消息段。当然,接收端收到数据后,也会对数据做效验,但效验完成后,如若数据存在差错,那 UDP
只会单纯的丢弃该数据包,不会要求发送端重发数据。
因为由于 UDP
高效的传输性能,因此常备应用在广播通知、音频通话、视频传输等多媒体数据流业务,而且这类业务中,如果有一个数据包丢失,在很短的时间内就会有另一个新的数据就会替换它,因此就算数据传输不可靠也无关紧要。
3.3.3、UDP中的丢包问题
由于 UDP
的不可靠传输,因此数据出现丢包是很常见的事情,一般 UDP
中造成数据丢包的原因主要如下:
UDP
丢包问题解决方案
TCP
3.3.4、TCP与UDP之间的区别
TCP | UDP |
---|---|
面向连接 | 面向无连接 |
可靠信息传输 | 不可靠信息传输 |
字节流传输 | 报文传输 |
传输速度慢 | 传输速度快 |
仅支持点对点通信 | 支持一对一、一对多、多对多通信 |
具备拥塞机制和流量控制 | 不具备拥塞机制和流量控制 |
TCP首部至少 20 个字节 |
UDP首部仅 8 个字节 |
腾讯QQ、微信两应用是国内社交通信软件中当之无愧的“霸主”,其中的QQ是一个基于 TCP/UDP
两个协议相结合的通讯软件。其中聊天首先采用 UDP
打洞方式,其次考虑 TCP
通信,当然,对于文件传输、表情包发送、QQ空间等是采用 TCP
传输。
曾经的老版QQ是可以自己选择登录方式的,也就是用户自己可以选 TCP
或 UDP
方式登录,如下:
但目前的新版QQ中,都是采用基于 TCP
协议的 HTTP
方式登录,即与我们本身做的登录原理大致相同,先通过调用后端接口获取令牌信息,然后认证.....,最后登录完成后,QQ会保持一条 TCP
连接来维持在线状态。
QQ的主要通信方式是基于 UDP
打洞技术,通过服务器中转方式实现的信息传输,这听起来是不是很不可思议?QQ作为“顶级”的聊天软件,怎么可以采用这样的不可靠方式来传输消息呢?
这里面牵扯到很多原因,有历史原因,也综合考虑了服务器负载、网络复杂度、服务器数量/网络带宽成本问题、实现难度等多方面因素导致的。
由于 UDP
传输不可靠,因此腾讯采用了上层协议来保证可靠传输:如果客户端使用 UDP
协议发出消息后,服务器收到该包,需要使用 UDP
协议发回一个应答包,通过这种方式来保证消息无遗漏的传输。
不过有时候之所以会看到“消息发送失败的红色感叹号”,但对方又收到了这个消息的情况,就是因为客户端发出的消息,服务器已经收到并转发成功,但客户端由于网络原因没有收到服务器的应答包引起的。
也包括网络比较差的情况下, QQ
信息发送之后,对方接收到的顺序跟你发出的不一致,这个也是由于 UDP
无序导致的。
当 A
想要向 B
传输一个文件时, A
首先会向服务器发出一个文件传输的请求,服务器接收到该请求后,会将其转发给 B
,同时会将 A
的临时 IP
发送给 B
,如果 A、B
两个客户端在同一内网,那么 B
会直接尝试连接 A
,从而达到文件传输的目的。
但如若 A、B
并不在同一个局域网,那么 A
的文件传输操作就会由服务器进行文件中转,因为服务器具备公网 IP
,因此 A、B
两个客户端都可以连接到服务器,于是 A、B
最终就通过这种中转的方式顺利达成文件传输的目的。
前面提到过,QQ的文字聊天几乎都是通过服务器中转的方式工作的,因为这种形式一方面可以保证“记录同步到云端”,另一方面也方便“刑事侦查”。
但音/视频通话,数据传输全经过服务器中转,对于服务器是一个沉重的负担,所以QQ的音/视频通话本质上是通过 NAT
穿透+ P2P
对等通信实现的,当然,在 P2P
直接通信失败时,会退而求次,服务器介入处理,通过中转模式实现传输。
大概过程如下:
A
打算与 B
进行视频通话,那会在“启动时”向服务器发送一个“通话请求”。IP/Prot
,然后将其相互交换。NAT
, A、B
双方根据服务器分发的地址建立连接。A、B
双方连接对端地址,然后相互传递数据,从而达到通话数据传输的目的。从如上过程中可以得出:QQ客户端之间“通话”,如果可以直接 P2P
通信,则服务器把他们各自的公网IP、端口号分发给彼此,这样他们的流量数据可以 P2P
传输,服务器在其中只分发QQ客户端 IP
、端口号信息,不会负担太大。但如果 P2P
通信失败,服务器会介入,采用服务器中转方式完成。
如果 NAT
无法穿透,那则无法进行 P2P
通信,无法穿透的原因也可能由于地理位置、网络类型限制等多方面因素导致。
P2P
通信翻译过来也就是端到端通信,在 P2P
模型中,服务器只作为客户端地址的中转站存在,不需要承担高昂的流量成本。
在上述过程中,我们只能大概分析QQ通信其中的原理,并不能保证这是腾讯内部具体的实现方案,因为细致的具体过程只有腾讯自己明白。也包括其具体实现定然复杂千百万倍,并非三言两语之间就可阐述清楚,因此对于QQ通信原理的分析部分,大概理解其原理即可。
在本章中,对于计算机网络的基础内容进行了全面阐述,从最初的计算机网络起源开始,到常见的网络体系、网络分层结构、 IP
网络协议、 TCP
可靠传输协议、 UDP
不可靠传输协议以及腾讯 QQ
软件原理分析等多块内容进行了分析,本章旨在先为后续篇章打下网络编程的基础,方便后续撰写其他内容时做好准备。