网络基础概括

网络基础

10.1 模型

10.1.1 OSI七层模型

图 10.1: OSI模型

1.物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介 质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到 达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比 特。

2.数据链路层:定义了如何让格式化数据以进行传输,以及如何让控制对物理介质的访 问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输。

3.网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选 择。Internet的发展使得从世界各站点访问信息的用户数大大增加,而网络层正是管理这种 连接的层。

4.传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协 议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报 协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就 是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后 再进行重组。常常把这一层数据叫做段。

5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你

98 第10章 网络基础

的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是 主机名)。

6.表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。 例如,PC程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码 (EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要, 表示层会通过使用一种通格式来实现多种数据格式之间的转换。

7.应用层:是最靠近用户的OSI层。这一层为用户的应用程序(例如电子邮件、文件传输 和终端仿真)提供网络服务。

10.1.2 TCP/IP四层模型

一般开发程序员讨论最多的是TCP/IP模型

10.2 通信过程

图 10.2: TCP/IP模型

图 10.3: TCP/IP通信过程

  上图对应两台计算机在同一网段中的情况,如果两台计算机在不同的网段中,那么数据
从一台计算机到另一台计算机传输过程中要经过一个或多个路由器,如下图所示

其实在链路层之下还有物理层,指的是电信号的传递方式,比如现在以太网通用的网线 (双绞线)、早期以太网采用的的同轴电缆(现在主要用于有线电视)、光纤等都属于物理 层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等。集线器(Hub)是 工作在物理层的网络设备,用于双绞线的连接和信号中继(将已衰减的信号再次放大使之传 得更远)。

10.2节 通信过程 99

图 10.4: 跨路由通信过程

  链路层有以太网、令牌环网等标准,链路层负责网卡设备的驱动、帧同步(就是说从网
线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差
错校验等工作。交换机是工作在链路层的网络设备,可以在不同的链路层网络之间转发数据
帧(比如十兆以太网和百兆以太网之间、以太网和令牌环网之间),由于不同链路层的帧格
式不同,交换机要将进来的数据包拆掉链路层首部重新封装之后再转发。

网络层的IP协议是构成Internet的基础。Internet上的主机通过IP地址来标识,Inter- net上有大量路由器负责根据IP地址选择合适的路径转发数据包,数据包从Internet上的源 主机到目的主机往往要经过十多个路由器。路由器是工作在第三层的网络设备,同时兼有交 换机的功能,可以在不同的链路层接口之间转发数据包,因此路由器需要将进来的数据包拆 掉网络层和链路层两层首部并重新封装。IP协议不保证传输的可靠性,数据包在传输过程中 可能丢失,可靠性可以在上层协议或应用程序中提供支持。

网络层负责点到点(point-to-point)的传输(这里的“点”指主机或路由器),而传 输层负责端到端(end-to-end)的传输(这里的“端”指源主机和目的主机)。传输层可选 择TCP或UDP协议。TCP是一种面向连接的、可靠的协议,有点像打电话,双方拿起电话互通 身份之后就建立了连接,然后说话就行了,这边说的话那边保证听得到,并且是按说话的顺 序听到的,说完话挂机断开连接。也就是说TCP传输的双方需要首先建立连接,之后由TCP协 议保证数据收发的可靠性,丢失的数据包自动重发,上层应用程序收到的总是可靠的数据 流,通讯之后关闭连接。UDP协议不面向连接,也不保证可靠性,有点像寄信,写好信放到 邮筒里,既不能保证信件在邮递过程中不会丢失,也不能保证信件是按顺序寄到目的地的。 使用UDP协议的应用程序需要自己完成丢包重发、消息排序等工作。

  目的主机收到数据包后,如何经过各层协议栈最后到达应用程序呢?整个过程如下图所
示

以太网驱动程序首先根据以太网首部中的“上层协议”字段确定该数据帧的有效载荷 (payload,指除去协议首部之外实际传输的数据)是IP、ARP还是RARP协议的数据报,然后 交给相应的协议处理。假如是IP数据报,IP协议再根据IP首部中的“上层协议”字段确定该 数据报的有效载荷是TCP、UDP、ICMP还是IGMP,然后交给相应的协议处理。假如是TCP段或 UDP段,TCP或UDP协议再根据TCP首部或UDP首部的“端口号”字段确定应该将应用层数据交 给哪个用户进程。IP地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不 同进程的地址,IP地址和端口号合起来标识网络中唯一的进程。

注意,虽然IP、ARP和RARP数据报都需要以太网驱动程序来封装成帧,但是从功能上划 分,ARP和RARP属于链路层,IP属于网络层。虽然ICMP、IGMP、TCP、UDP的数据都需要IP协 议来封装成数据报,但是从功能上划分,ICMP、IGMP与IP同属于网络层,TCP和UDP属于传输层

100 第10章 网络基础

图 10.5: Multiplexing过程

本文对RARP、ICMP、IGMP协议不做进一步介绍,有兴趣的读者可以看参考资料。

10.3 协议格式

10.3.1 数据包封装

传输层及其以下的机制由内核提供,应用层由用户进程提供(后面将介绍如何使用 socket API编写应用程序),应用程序对通讯数据的含义进行解释,而传输层及其以下 处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机。应用层 数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装 (Encapsulation),如下图所示

图 10.6: TCP/TP数据包封装

不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数 据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目 的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。

10.3.2 以太网帧格式

其中的源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位,是在网 卡出厂时固化的。用ifconfig命令看一下,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地 址。协议字段有三种值,分别对应IP、ARP、RARP。帧末尾是CRC校验码。

图 10.7:  以太网帧格式

以太网帧中的数据长度规定最小46字节,最大1500字节,ARP和RARP数据包的长度不够 46字节,要在后面补填充位。最大值1500称为以太网的最大传输单元(MTU),不同的网络 类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路 的MTU了,则需要对数据包进行分片(fragmentation)。ifconfig命令的输出中也有“MTU: 1500”。注意,MTU这个概念指数据帧中有效载荷的最大长度,不包括帧首部的长度。

10.3.3 ARP数据报格式

在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机 的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的 硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议 就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多 少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示 广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应 答数据包给源主机,将自己的硬件地址填写在应答包中。

每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间 (一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP 请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?

ARP数据报的格式如下所示


图 10.8: ARP数据报格式

注意到源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为 以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链 路层网络类型,1为以太网,协议类型指要转换的地址类型,0x0800为IP地址,后面两个地 址长度对于以太网地址和IP地址分别为6和4(字节),op字段为1表示ARP请求,op字段为2 表示ARP应答。

下面举一个具体的例子。 请求帧如下(为了清晰在每行的前面加了字节计数,每行16个字节): 以太网首部(14字节)
0000: ffffffffffff00055d6158a80806 ARP帧(28字节)
0000: 00 01
0010: 08000604000100055d6158a8c0a80037

10.3节 协议格式 101

102 第10章 网络基础

0020: 000000000000c0a80002
填充位(18字节)
0020: 00 77 31 d2 50 10
0030: fd7841d30000000000000000 以太网首部:目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协

议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC地

址)长度为6,协议地址(IP地址)长度为4,op为0x0001表示请求目的主机的MAC地址,源 主机MAC地址为00:05:5d:61:58:a8,源主机IP地址为c0 a8 00 37(192.168.0.55),目的 主机MAC地址全0待填写,目的主机IP地址为c0 a8 00 02(192.168.0.2)。

由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充 位,填充位的内容没有定义,与具体实现相关。

应答帧如下:
以太网首部
0000: 00055d6158a800055da1b8400806
ARP帧
0000: 00 01
0010: 08000604000200055da1b840c0a80002
0020: 00055d6158a8c0a80037
填充位
0020: 00 77 31 d2 50 10
0030: fd7841d30000000000000000 以太网首部:目的主机的MAC地址是00:05:5d:61:58:a8,源主机的MAC地址是00:05:5d:a1:b8:40,

上层协议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC

地址)长度为6,协议地址(IP地址)长度为4,op为0x0002表示应答,源主机MAC地址为 00:05:5d:a1:b8:40,源主机IP地址为c0 a8 00 02(192.168.0.2),目的主机MAC地址为 00:05:5d:61:58:a8,目的主机IP地址为c0 a8 00 37(192.168.0.55)。

思考题:如果源主机和目的主机不在同一网段,ARP请求的广播帧无法穿过路由器,源 主机如何与目的主机通信?

10.3.4 IP段格式

图 10.9: IP数据报格式

IP数据报的首部长度和数据长度都是可变长的,但总是4字节的整数倍。对于IPv4,4 位版本字段是4。4位首部长度的数值是以4字节为单位的,最小值为5,也就是说首部长度 最小是4x5=20字节,也就是不带任何选项的IP首部,4位能表示的最大值是15,也就是说首 部长度最大是60字节。8位TOS字段有3个位用来指定IP数据报的优先级(目前已经废弃不 用),还有4个位表示可选的服务类型(最小延迟、最大吐量、最大可靠性、最小成本), 还有一个位总是0。总长度是整个数据报(包括IP首部和IP层payload)的字节数。每传一 个IP数据报,16位的标识加1,可用于分片和重新组装数据报。3位标志和13位片偏移用于 分片。TTL(Time to live)是这样用的:源主机为数据包设定一个生存时间,比如64,每过 一个路由器就把该值减1,如果减到0就表示路由已经太长了仍然找不到目的主机的网络, 就丢弃该包,因此这个生存时间的单位不是秒,而是跳(hop)。协议字段指示上层协议 是TCP、UDP、ICMP还是IGMP。然后是校验和,只校验IP首部,数据的校验由更高层协议负 责。IPv4的IP地址长度为32位。选项字段的解释从略。

想一想,前面讲了以太网帧中的最小数据长度为46字节,不足46字节的要用填充字节补 上,那么如何界定这46字节里前多少个字节是IP、ARP或RARP数据报而后面是填充字节?

10.3.5 UDP数据抱格式


图 10.10: UDP数据段

下面分析一帧基于UDP的TFTP协议帧。
以太网首部
0000: 00055d67d0b100055d6158a80800
IP首部
0000: 45 00
0010: 005393250000801125ecc0a80037c0a8
0020: 00 01
UDP首部
0020: 05 d4 00 45 00 3f ac 40
TFTP协议
0020: 00 01 ‘c”:‘”’q’
0030: ‘w’‘e’‘r’‘q”.‘’q’‘w’‘e’00 ’n’‘e”t’‘a”s’‘c’‘i’ 0040: ‘i’00 ’b’‘l’‘k”s’‘i’‘z’‘e’00 ’5’‘1’‘2’00 ’t’‘i’ 0050: ’m’‘e’‘o’‘u”t’00 ‘1’‘0’00 ’t”s’‘i’‘z’‘e’00 ’0’ 0060: 00 以太网首部:源MAC地址是00:05:5d:61:58:a8,目的MAC地址是00:05:5d:67:d0:b1,上

层协议类型0x0800表示IP。

 IP首部:每一个字节0x45包含4位版本号和4位首部长度,版本号为4,即IPv4,首部长度为5,说明IP首部不带有选项字段。服务类型为0,没有使用服务。16位总长度字段(包 括IP首部和IP层payload的长度)为0x0053,即83字节,加上以太网首部14字节可知整个帧

长度是97字节。IP报标识是0x9325,标志字段和片偏移字段设置为0x0000,就是DF=0允许分 片,MF=0此数据报没有更多分片,没有分片偏移。TTL是0x80,也就是128。上层协议0x11表 示UDP协议。IP首部校验和为0x25ec,源主机IP是c0 a8 00 37(192.168.0.55),目的主机 IP是c0 a8 00 01(192.168.0.1)。

UDP首部:源端口号0x05d4(1492)是客户端的端口号,目的端口号0x0045(69)是 TFTP服务的well-known端口号。UDP报长度为0x003f,即63字节,包括UDP首部和UDP层pay- load的长度。UDP首部和UDP层payload的校验和为0xac40。

TFTP是基于文本的协议,各字段之间用字节0分隔,开头的00 01表示请求读取一个文 件,接下来的各字段是:

c:\qwerq.qwe

 netascii blksize 512 

timeout 10 

tsize 0

一般的网络通信都是像TFTP协议这样,通信的双方分别是客户端和服务器,客户端主动 发起请求(上面的例子就是客户端发起的请求帧),而服务器被动地等待、接收和应答请 求。客户端的IP地址和端口号唯一标识了该主机上的TFTP客户端进程,服务器的IP地址和端 口号唯一标识了该主机上的TFTP服务进程,由于客户端是主动发起请求的一方,它必须知道 服务器的IP地址和TFTP服务进程的端口号,所以,一些常见的网络协议有默认的服务器端 口,例如HTTP服务默认TCP协议的80端口,FTP服务默认TCP协议的21端口,TFTP服务默认UDP 协议的69端口(如上例所示)。在使用客户端程序时,必须指定服务器的主机名或IP地址, 如果不明确指定端口号则采用默认端口,请读者查阅ftp、tftp等程序的man page了解如何 指定端口号。/etc/services中列出了所有well-known的服务端口和对应的传输层协议,这 是由IANA(Internet Assigned Numbers Authority)规定的,其中有些服务既可以用TCP也 可以用UDP,为了清晰,IANA规定这样的服务采用相同的TCP或UDP默认端口号,而另外一些 TCP和UDP的相同端口号却对应不同的服务。

很多服务有well-known的端口号,然而客户端程序的端口号却不必是well-known的, 往往是每次运行客户端程序时由系统自动分配一个空闲的端口号,用完就释放掉,称为 ephemeral的端口号,想想这是为什么。

前面提过,UDP协议不面向连接,也不保证传输的可靠性,例如:

发送端的UDP协议层只管把应用层传来的数据封装成段交给IP协议层就算完成任务了, 如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息。

接收端的UDP协议层只管把收到的数据根据端口号交给相应的应用程序就算完成任务 了,如果发送端发来多个数据包并且在网络上经过不同的路由,到达接收端时顺序已经错乱 了,UDP协议层也不保证按发送时的顺序交给应用层。

通常接收端的UDP协议层将收到的数据放在一个固定大小的缓冲区中等待应用程序来提 取和处理,如果应用程序提取和处理的速度很慢,而发送端发送的速度很快,就会丢失数据 包,UDP协议层并不报告这种错误。

因此,使用UDP协议的应用程序必须考虑到这些可能的问题并实现适当的解决方案,例 如等待应答、超时重发、为数据包编号、流量控制等。一般使用UDP协议的应用程序实现都 比较简单,只是发送一些对可靠性要求不高的消息,而不发送大量的数据。例如,基于UDP 的TFTP协议一般只用于传送小文件(所以才叫trivial的ftp),而基于TCP的FTP协议适用于 各种文件的传输。下面看TCP协议如何用面向连接的服务来代替应用程序解决传输的可靠性问题。

10.3.6 TCP数据报格式

        图 10.11: TCP数据段

和UDP协议一样也有源端口号和目的端口号,通讯的双方由IP地址和端口号标识。32位 序号、32位确认序号、窗口大小稍后详细解释。4位首部长度和IP协议头类似,表示TCP协 议头的长度,以4字节为单位,因此TCP协议头最长可以是4x15=60字节,如果没有选项字 段,TCP协议头最短20字节。URG、ACK、PSH、RST、SYN、FIN是六个控制位,本节稍后将解 释SYN、ACK、FIN、RST四个位,其它位的解释从略。16位检验和将TCP协议头和数据都计算 在内。紧急指针和各种选项的解释从略。

通信时序 下图是一次TCP通讯的时序图。 在这个例子中,首先客户端主动发起连接、发送请求,然后服务器端响应请求,然后客

户端主动关闭连接。两条竖线表示通讯的两端,从上到下表示时间的先后顺序,注意,数据 从一端传到网络的另一端也需要时间,所以图中的箭头都是斜的。双方发送的段按时间顺 序编号为1-10,各段中的主要信息在箭头上标出,例如段2的箭头上标着SYN, 8000(0), ACK 1001, ,表示该段中的SYN位置1,32位序号是8000,该段不携带有效载荷(数据字节数为 0),ACK位置1,32位确认序号是1001,带有一个mss选项值为1024。

建立连接的过程:

1.客户端发出段1,SYN位表示连接请求。序号是1000,这个序号在网络通讯中用作临时 的地址,每发一个数据字节,这个序号要加1,这样在接收端可以根据序号排出数据包的正 确顺序,也可以发现丢包的情况,另外,规定SYN位和FIN位也要占一个序号,这次虽然没发 数据,但是由于发了SYN位,因此下次再发送应该用序号1001。mss表示最大段尺寸,如果一 个段太大,封装成帧后超过了链路层的最大帧长度,就必须在IP层分片,为了避免这种情 况,客户端声明自己的最大段尺寸,建议服务器端发来的段不要超过这个长度。

2.服务器发出段2,也带有SYN位,同时置ACK位表示确认,确认序号是1001,表示“我 接收到序号1000及其以前所有的段,请你下次发送序号为1001的段”,也就是应答了客户端 的连接请求,同时也给客户端发出一个连接请求,同时声明最大尺寸为1024。

3.客户端发出段3,对服务器的连接请求进行应答,确认序号是8001。

在这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求, 其中服务器的请求和应答在一个段中发出,因此一共有三个段用于建立连接,称为’‘’三 方握手(three-way-handshake)”’。在建立连接的同时,双方协商了一些信息,例如双 方发送序号的初始值、最大段尺寸等。

106 第10章 网络基础

图 10.12: TCP连接建立断开

在TCP通讯中,如果一方收到另一方发来的段,读出其中的目的端口号,发现本机并没 有任何进程使用这个端口,就会应答一个包含RST位的段给另一方。

数据传输的过程:
1.客户端发出段4,包含从序号1001开始的20个字节数据。 2.服务器发出段5,确认序号为1021,对序号为1001-1020的数据表示确认收到,同时请

求发送序号1021开始的数据,服务器在应答的同时也向客户端发送从序号8001开始的10个字 节数据,这称为piggyback。

3.客户端发出段6,对服务器发来的序号为8001-8010的数据表示确认收到,请求发送序 号8011开始的数据。

关闭连接的过程:
1.客户端发出段7,FIN位表示关闭连接的请求。 

2.服务器发出段8,应答客户端的关闭连接请求。 

3.服务器发出段9,其中也包含FIN位,向客户端发送关闭连接请求。 

4.客户端发出段10,应答服务器的关闭连接请求。

10.4 再议TCP

10.4.1 tcp状态转换图

这个图N多人都知道,它排除和定位网络或系统故障时大有帮助,但是怎样牢牢地将这 张图刻在脑中呢?那么你就一定要对这张图的每一个状态,及转换的过程有深刻 的认识, 不能只停留在一知半解之中。下面对这张图的11种状态详细解析一下,以便加强记忆!不过 在这之前,先回顾一下TCP建立连接的三次握手过程,以及 关闭连接的四次握手过程。

10.4节 再议TCP 107

图 10.13: TCP状态转换图

108 第10章 网络基础

1、建立连接协议(三次握手) (1)客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。 (2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志

和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户 端是否准备好进行数据通讯。

(3) 客户必须再次回应服务段一个ACK报文,这是报文段3。 2、连接终止协议(四次握手)
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完

成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这 一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方 将执行主动关闭,而另一方执行被动关闭。

(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。

(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。 和SYN一样,一个FIN将占用一个序号。

(3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。
(4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

CLOSED: 这个没什么好说的了,表示初始状态。

LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。

SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次 握手会话过程中的一个中间状态,很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户 端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。

SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即 它会进入到了SYN_SENT状 态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN 报文。

ESTABLISHED:这个容易理解了,表示连接已经建立了。

FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报 文。而这两种状态的区别 是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向 对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状 态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以FIN_WAIT_1状态一般是比较 难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求 close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果 FIN_WAIT_1状态下,收到了对方同时带 FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过 FIN_WAIT_2状态。

CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送 FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表 示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什 么情况下会出现此种情况 呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时 发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自 己,你系统毫无疑问地会回应一个ACK报文给对 方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要 考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文 给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收 到ACK报文后,也即可以进入到CLOSED可用状态了。

10.4.2 TCP流量控制(滑动窗口)

介绍UDP时我们描述了这样的问题:如果发送端发送的速度较快,接收端接收到数据后 处理的速度较慢,而接收缓冲区的大小是固定的,就会丢失数据。TCP协议通过’滑动窗口 (Sliding Window)’机制解决这一问题。看下图的通讯过程。


图 10.14: 滑动窗口

1.发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我 的接收缓冲区还有4K字节空闲,你发的数据不要超过4K”。接收端应答连接请求,声明最大 段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。

2.发送端发出段4-9,每个段带1K的数据,发送端根据窗口大小知道接收端的缓冲区满 了,因此停止发送数据。

3.接收端的应用程序提走2K数据,接收缓冲区又有了2K空闲,接收端发出段10,在应答 已收到6K数据的同时声明窗口大小为2K。

4.接收端的应用程序又提走2K数据,接收缓冲区有4K空闲,接收端发出段11,重新声明 窗口大小为4K。

5.发送端发出段12-13,每个段带2K数据,段13同时还包含FIN位。

6.接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答 序号是8194,连接处于半关闭状态,接收端同时声明窗口大小为2K。

7.接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。

8.接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为 6K。

9.接收端的应用程序在提走全部数据后,决定关闭连接,发出段17包含FIN位,发送端 应答,连接完全关闭。

上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接 收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序 提走数据,虚线框是向右滑动的,因此称为滑动窗口。

从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两 K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就 是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通讯中这些数 据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此 TCP协议是面向流的协议。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必 须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

10.4.3 TCP半链接状态

当TCP链接中A发送FIN请求关闭,另一段B回应ACK后,B没有立即发送FIN给A时,A方处 在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。

#include <sys/socket.h>
int shutdown(int sockfd, int how);

sockfd: 需要关闭的socket的描述符 how:允许为shutdown操作选择以下几种方式:

SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。 进程将不能对该套接字发出任何读操作。对 TCP套接字该调用之后接受到的任何数据将被确认然后无声的

丢弃掉。
SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作 SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR

使用close中止一个连接,但它只是减少描述符的引用计数,并不直接关闭连接,只有 当描述符的引用计数为0时才关闭连接。

shutdown可直接关闭描述符,不考虑描述符的引用计数,可选择中止一个方向的连接。

注意: 1>. 如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计 数为0时,也就是所用进程都调用了close,套接字将被释放。 2>. 在多进程中如果一个进 程中shutdown(sfd, SHUT_RDWR)后其它的进程将无法进行通信. 如果一个进程close(sfd) 将不会影响到其它进程. 得自己理解引用计数的用法了

10.4.4 2MSL

2MSL TIME_WAIT状态存在的理由:

TIME_WAIT状态的存在有两个理由:(1)让4次握手关闭流程更加可靠;4次握手的最 后一个ACK是是由主动关闭方发送出去的,若这个ACK丢失,被动关闭方会再次发一个FIN

过来。若主动关闭方能够保持一个2MSL的TIME_WAIT状态,则有更大的机会让丢失的ACK 被再次发送出去。(2)防止lost duplicate对后续新建正常链接的传输造成破坏。lost duplicate在实际的网络中非常常见,经常是由于路由器产生故障,路径无法收敛,导致一 个packet在路由器A,B,C之间做类似死循环的跳转。IP头部有个TTL,限制了一个包在网 络中的最大跳数,因此这个包有两种命运,要么最后TTL变为0,在网络中消失;要么TTL在 变为0之前路由器路径收敛,它凭借剩余的TTL跳数终于到达目的地。但非常可惜的是TCP 通过超时重传机制在早些时候发送了一个跟它一模一样的包,并先于它达到了目的地,因 此它的命运也就注定被TCP协议栈抛弃。另外一个概念叫做incarnation connection,指跟 上次的socket pair一摸一样的新连接,叫做incarnation of previous connection。lost duplicate加上incarnation connection,则会对我们的传输造成致命的错误。大家都知道 TCP是流式的,所有包到达的顺序是不一致的,依靠序列号由TCP协议栈做顺序的拼接;假设 一个incarnation connection这时收到的seq=1000, 来了一个lost duplicate为seq=1000, len=1000, 则tcp认为这个lost duplicate合法,并存放入了receive buffer,导致传输出 现错误。通过一个2MSL TIME_WAIT状态,确保所有的lost duplicate都会消失掉,避免对新 连接造成错误。

该状态为什么设计在主动关闭这一方:
(1)发最后ack的是主动关闭一方 

(2)只要有一方保持TIME_WAIT状态,就能起到避免incarnation connection在2MSL内

的重新建立,不需要两方都有 如何正确对待2MSL TIME_WAIT?

RFC要求socket pair在处于TIME_WAIT时,不能再起一个incarnation connection。但绝大部分TCP实现,强加了 更为严格的限制。在2MSL等待期间,socket中使用的本地端口在默认情况下不能再被使用。若A 10.234.5.5:1234 和B 10.55.55.60:6666建立了连接,A主动关闭,那么在A端只要port为1234,无论对方的port和ip是什么,都不 允许再起服务。显而易见这是比RFC更为严格的限制,RFC仅仅是要求socket pair不一致,而实现当中只要这个 port处于TIME_WAIT,就不允许起连接。这个限制对主动打开方来说是无所谓的,因为一般用的是临时端口;但对 于被动打开方,一般是server,就悲剧了,因为server一般是熟知端口。比如http,一般端口是80,不可能允许 这个服务在2MSL内不能起来。解决方案是给服务器的socket设置SO_REUSEADDR选项,这样的话就算熟知端口处于 TIME_WAIT状态,在这个端口上依旧可以将服务启动。当然,虽然有了SO_REUSEADDR选项,但sockt pair这个限制 依旧存在。比如上面的例子,A通过SO_REUSEADDR选项依旧在1234端口上起了监听,但这时我们若是从B通过6666端 口去连它,TCP协议会告诉我们连接失败,原因为Address already in use.

        RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

实例 现在做一个测试,首先启动server,然后启动client,然后用Ctrl-C使server终止, 这时马上再运行server,结果是:

itcast$ ./server
bind error: Address already in use

这是因为,虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此 不能再次监听同样的server端口。我们用netstat命令查看一下:

itcast$ netstat -apn |grep 8000

tcp 1 0 127.0.0.1:33498 127.0.0.1:8000 CLOSE_WAIT 10830/client 

tcp 0 0 127.0.0.1:8000 127.0.0.1:33498 FIN_WAIT2 -

server终止时,socket描述符会自动关闭并发FIN段给client,client收到FIN后处于 CLOSE_WAIT状态,但是client并没有终止,也没有关闭socket描述符,因此不会发FIN给 server,因此server的TCP连接处于FIN_WAIT2状态。

现在用Ctrl-C把client也终止掉,再观察现象:

client终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处 于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个 MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止 了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的 server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般 经过半分钟后就可以再次启动server了。至于为什么要规定TIME_WAIT的时间,参考UNP 2.7 节。

在server的TCP连接没有完全断开之前不允许重新监听是不合理的,因为,TCP连接 没有完全断开指的是connfd(127.0.0.1:8000)没有完全断开,而我们重新监听的是lis- tenfd(0.0.0.0:8000),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个 客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的 方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号 相同但IP地址不同的多个socket描述符。在server代码的socket()和bind()调用之间插入如 下代码:

int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));


10.4.5 检测tcp一端是否异常断开

心跳检测机制 在TCP网络通信中,经常会出现客户端和服务器之间的非正常断开,需要实 时检测查询链接状态。常用的解决方法就是在程序中加入心跳机制。

Heart-Beat线程

这个是最常用的简单方法。在接收和发送数据时个人设计一个守护进程(线程),定时发 送Heart-Beat包,客户端/服务器收到该小包后,立刻返回相应的包即可检测对方是否实时 在线。

  该方法的好处是通用,但缺点就是会改变现有的通讯协议!大家一般都是使用业务层心
跳来处理,主要是灵活可控。

UNIX网络编程不推荐使用SO_KEEPALIVE来做心跳检测,还是在业务层以心跳包做检测比 较好,也方便控制,

设置tcp的属性 SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻 塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交 换,TCP就自 动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须 响应的TCP分节.它会导致以下三种情况:对方接收一切正常:以期望的ACK响应。2小时 后,TCP将发出另一个探测分 节。对方已崩溃且已重新启动:以RST响应。套接口的待处理 错误被置为ECONNRESET,套接 口本身则被关闭。对方无任何响应:源自berkeley的TCP发送 另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟 15秒 后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。如ICMP 错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种 情况下待处理错误被置为 EHOSTUNREACH。

根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候,我们可以设置 SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。

keepAlive = 1;
Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

如果我们不能接受如此之长的等待时间,从TCP-Keepalive-HOWTO上可以知道一共有两 种方式可以设置,一种是修改内核关于网络方面的 配置参数,另外一种就是SOL_TCP字段的 TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT三个选项。

1.The tcp_keepidle parameter specifies the interval of inactivity that causes TCP to generate a KEEPALIVE transmission for an application that requests them. tcp_keepidle defaults to 14400 (two hours). /*开始首次KeepAlive探测前的TCP空闭时 间 */

2.The tcp_keepintvl parameter specifies the interval between the nine retries that are attempted if a KEEPALIVE transmission is not acknowledged. tcp_keepintvl defaults to 150 (75 seconds). /* 两次KeepAlive探测间的时间间隔 */

3.The tcp_keepcnt option specifies the maximum number of keepalive probes to be sent. The value of TCP_KEEPCNT is an integer value between 1 and n, where n is the value of the systemwide tcp_keepcnt parameter. /* 判定断开前的KeepAlive探 测次数*/

int keepIdle = 1000; 

int keepInterval = 10;

 int keepCount = 10;

Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle)); 

Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval)); 

Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

SO_KEEPALIVE设置空闲2小时才发送一个“保持存活探测分节”,不能保证实时检测。 对于判断网络断开时间太长,对于需要及时响应的程序不太适应。

当然也可以修改时间间隔参数,但是会影响到所有打开此选项的套接口!关联了完成端 口的socket可能会忽略掉该套接字选项。

10.5 名词术语解析

10.5.1 什么是路由(route)

1.网络信息从信源到信宿的路径.路由是指路由器从一个接口上收到数据包,根据数据 包的目的地址进行定向并转发到另一个接口的过程。

2.路由通常与桥接来对比,在粗心的人看来,它们似乎完成的是同样的事。它们的主 要区别在于桥接发生在OSI参考模型的第二层(数据链路层),而路由发生在第三层(网络 层)。这一区别使二者在传递信息的过程中使用不同的信息,从而以不同的方式来完成其任 务。

3.确定最佳路径,通过网络传输信息

10.5.2 路由器工作原理

路由器(Router)是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动 选择和设定路由,以最佳路径,按前后顺序发送信号的设备。

传统地,路由器工作于OSI七层协议中的第三层,其主要任务是接收来自一个网络接口 的数据包,根据其中所含的目的地址,决定转发到下一个目的地址。因此,路由器首先得在 转发路由表中查找它的目的地址,若找到了目的地址,就在数据包的帧格前添加下一个MAC 地址,同时IP数据包头的TTL(Time To Live)域也开始减数, 并重新计算校验和。当数据 包被送到输出端口时,它需要按顺序等待,以便被传送到输出链路上。

  路由器在工作时能够按照某种路由通信协议查找设备中的路由表。如果到某一特定节点
有一条以上的路径,则基本预先确定的路由准则是选择最优(或最经济)的传输路径。由于
各种网络段和其相互连接情况可能会因环境变化而变化,因此路由情况的信息一般也按所使
用的路由信息协议的规定而定时更新。
  网络中,每个路由器的基本功能都是按照一定的规则来动态地更新它所保持的路由表,
以便保持路由信息的有效性。为了便于在网络间传送报文,路由器总是先按照预定的规则把
较大的数据分解成适当大小的数据包,再将这些数据包分别通过相同或不同路径发送出去。
当这些数据包按先后秩序到达目的地后,再把分解的数据包按照一定顺序包装成原有的报文
形式。路由器的分层寻址功能是路由器的重要功能之一,该功能可以帮助具有很多节点站的
网络来存储寻址信息,同时还能在网络间截获发送到远地网段的报文,起转发作用;选择最
合理的路由,引导通信也是路由器基本功能;多协议路由器还可以连接使用不同通信协议的
网络段,成为不同通信协议网络段之间的通信平台。

路由和交换之间的主要区别就是交换发生在OSI参考模型第二层(数据链路层),而路 由发生在第三层,即网络层。这一区别决定了路由和交换在移动信息的过程 中需使用不同 的控制信息,所以两者实现各自功能的方式是不同的。

10.5.3 路由表(Routing Table)

在计算机网络中,路由表或称路由择域信息库(RIB)是一个存储在路由器或者联网计 算机中的电子表格(文件)或类数据库。路由表存储着指向特定网络地址的路径

10.5.4 以太网交换机工作原理

以太网交换机是基于以太网传输数据的交换机,以太网采用共享总线型传输媒体方式的 局域网。以太网交换机的结构是每个端口都直接与主机相连,并且一般都工作在全双工方

式。交换机能同时连通许多对端口,使每一对相互通信的主机都能像独占通信媒体那样,进
行无冲突地传输数据。

以太网交换机工作于OSI网络参考模型的第二层(即数据链路层),是一种基于 MAC(Media Access Control,介质访问控制)地址识别、完成以太网数据帧转发的网络 设备。

10.5.5 hub工作原理

  集线器实际上就是中继器的一种,其区别仅在于集线器能够提供更多的端口服务,所以
集线器又叫多口中继器。

集线器功能是随机选出某一端口的设备,并让它独占全部带宽,与集线器的上联设备 (交换机、路由器或服务器等)进行通信。从Hub的工作方式可以看出,它在网络中只起到信 号放大和重发作用,其目的是扩大网络的传输范围,而不具备信号的定向传送能力,是—个 标准的共享式设备。其次是Hub只与它的上联设备(如上层Hub、交换机或服务器)进行通信, 同层的各端口之间不会直接进行通信,而是通过上联设备再将信息广播到所有端口上。 由 此可见,即使是在同一Hub的不同两个端口之间进行通信,都必须要经过两步操作:第一步 是将信息上传到上联设备;第二步是上联设备再将该信息广播到所有端口上.

10.5.6 半双工/全双工

Full-duplex(全双工)全双工是在通道中同时双向数据传输的能力。 Half-duplex(半双工)在通道中同时只能沿着一个方向传输数据。

10.5.7 DNS服务器

DNS 是域名系统 (Domain Name System) 的缩写,是因特网的一项核心服务,它作为可 以将域名和IP地址相互映射的一个分布式数据库,能够使人更方便的访问互联网,而不用去 记住能够被机器直接读取的IP地址串。

它是由解析器以及域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名 和对应IP地址,并具有将域名转换为IP地址功能的服务器。

10.5.8 局域网(local area network;LAN)

  一种覆盖一座或几座大楼、一个校园或者一个厂区等地理区域的小范围的计算机网

(1)覆盖的地理范围较小,只在一个相对独立的局部范围内联,如一座或集中的建筑 群内。

(2)使用专门铺设的传输介质进行联网,数据传输速率高(10Mb/s~10Gb/s) (3)通信延迟时间短,可靠性较高
(4)局域网可以支持多种传输介质

10.5.9 广域网(wide area network;WAN)

  一种用来实现不同地区的局域网或城域网的互连,可提供不同地区、城市和国家之间的
计算机通信的远程计算机网。

覆盖的范围比局域网(LAN)和城域网(MAN)都广。广域网[1]的通信子网主要使用分 组交换技术。

10.5节 名词术语解析 115

116 第10章 网络基础

  广域网的通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网,它将分布
在不同地区的局域网或计算机系统互连起来,达到资源共享的目的。如互联网是世界范围内
最大的广域网。

1、适应大容量与突发性通信的要求; 2、适应综合业务服务的要求; 3、开放的设备接口与规范化的协议; 4、完善的通信服务与网络管理。

10.5.10 端口

逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用 于浏览网页服务的80端口,用于FTP服务的21端口等等。

(1)端口号小于256的定义为常用端口,服务器一般都是通过常用端口号来识别的。

(2)客户端只需保证该端口号在本机上是惟一的就可以了。客户端口号因存在时间很 短暂又称临时端口号;

(3)大多数TCP/IP实现给临时端口号分配1024—5000之间的端口号。大于5000的端口 号是为其他服务器预留的。

10.5.11 MTU

MTU:通信术语 最大传输单元(Maximum Transmission Unit,MTU)

  是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。最大传
输单元这个参数通常与通信接口有关(网络接口卡、串口等)。

以下是一些协议的MTU

FDDI协议:4352字节 

以太网(Ethernet)协议:1500字节 PPPoE(ADSL)协议:1492字节 X.25协议(Dial Up/Modem):576字节 Point-to-Point:4470字节

10.6 常见网络知识面试题:

1.TCP如何建立链接
2.TCP如何通信
3.TCP如何关闭链接
4.什么是滑动窗口
5.什么是半关闭 6.局域网内两台机器如何利用tcp/ip通信? 7.internet上两台主机如何进行通信 8.如何在internet上识别唯一一个进程 答:通过“IP地址+端口号”来区 分不同的服务的 9.为什么说tcp是可靠的链接,udp不可靠 10.路由器和交换机的区别

11.点到点,端到端



猜你喜欢

转载自blog.csdn.net/lanlan1266/article/details/80166104