IP首部有一个协议字段,用来标识网络层的上一层所采用的是哪一种传输层协议(TCP或UDP)。
使用端口号可以识别在传输层上一层的应用层中所要进行处理的具体程序(一个程序可以使用多个端口)。
通信处理
TCP/IP的众多应用协议大多以客户端/服务端形式运行。作为服务端的程序有必要提前启动,准备接受客户端的请求。否则即使客户端的请求发过来,也无法做到相应的处理。
这些服务器程序在UNIX系统中叫做守护进程。例如 HTTP的服务端程序是 httpd ,ssh 的服务端程序是sshd 。
在UNIX中并不需要将这些守护进程逐个启动,而是启动一个可以代表他们接收客户端请求的inetd(互联网守护进程)服务程序即可。它是一种超级守护进程。该超级守护进程接收到客户端请求后会fork
新的进程并转换为(exec
)为httpd、sshd等各个守护进程。
确认一个请求是发给哪个服务端(守护进程),可以通过收到数据包的目标端口号识别。
TCP和UDP
- TCP
TCP是面向连接的、可靠的流协议。流就是不间断的数据结构(字节流)。
采用TCP发送消息时,虽然可以保证发送的顺序,但还是像没有任何间隔的数据流发送给接收端。
e.g. 在发送端应用程序发送了10次100字节的消息,在接收端可能会收到1000字节连续不间断的数据。所以在TCP通信中,发送端应用可在消息中设置一个表示长度或间隔的字段信息。
- UDP
UDP是不具有可靠性的数据报协议。细微的处理会交给上层的应用去完成。在UDP情况下,虽然可以确保发送消息的大小,却不能保证消息一定会到达。因此有时候应用要自行重发。
e.g. 如果发送端应用程序发送一个100字节的消息,那么接收端应用程序也会以100字节为长度接收数据。在UDP中,消息长度的数据也会发送到接收端,因此在发送的消息中不需要设置表示消息长度或间隔的字段信息。
6.2 端口号
端口号用来识别同一台计算机中进行通信的不同应用程序。
TCP/IP或UDP/IP通信中通常采用五个信息来识别一个通信:源IP地址、目标IP地址、协议号、源端口号、目标端口号,只要其中某一项不同,就被认为是两个不同的连接。
端口号如何确定
- 标准既定的端口号
周知端口号:例如HTTP、FTP等,端口号就是固定的,一般在0~1023间。
还有一些端口号也被正式注册,分布1024~49151间。
- 时序分配法
服务段有必要确定监听端口号,但是接受服务的客户端没必要确定端口号。在这种发发下,客户端应用程序不用自己设置端口号,而交给OS进行分配。
动态分配的端口号分布在49152~65535间。
6.3 UDP
UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的数据在收到的那一刻,立刻按原样发送到网络上的机制。
即使出现网络拥堵情况,UDP也无法进行流量控制;传输途中出现丢包,UDP也不负责重发;当出现包到达顺序乱掉时也没有纠正功能。如果需要的话,得交由采用UDP的应用程序去处理。
由于UDP面向无连接,可以随时发送数据,主要用于:
- 包总量较少的通信:DNS、SNMP等
- 视频、音频直播等多媒体即时通信
- 限定于LAN等特定网络中的应用通信
- 广播通信(广播、多播)
6.4 TCP
TCP可以进行丢包时的重发控制,可以对次序乱掉的分包进行顺序控制。此外,TCP作为面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而控制通信流量的浪费。
通过序列号与确认应答ACK提高可靠性
在TCP中,发送端数据到达接收主机时,接收端主机会返回一个已收到消息的通知。该消息叫确认应答(ACK)。
当然,未收到确认应答也并不意味着数据一定丢失。可能是对方已收到数据,只是返回的确认应答在途中丢失。这种情况也会导致发送端重发数据。
通过序列号可以实现确认应答处理、重发控制以及重复控制。序列号是按顺序给发送数据的每一个字节都表上号码的编号(序列号的初始值并非为0,而是在建立连接后由随机数生成)。接收端查询接收数据TCP首部中的序列号和数据的长度,将自己下一步应该接受的序号作为确认应答返送回去。
TCP的数据长度并未写入TCP首部。实际通信中求得的TCP包的长度的计算公式是:**IP首部中的数据包长度-IP首部长度-TCP首部长度**
。
重发超时如何确定
TCP要求不论处在何种网络环境下都要提供高性能通信。所以它在每次发包时都会计算往返时间(RTT,报文段往返时间),以及其偏差。
数据被重发之后若还是收不到确认应答,则进行再次发送。此时,等待确认应答的时间将会以2倍、4倍的指数函数延长。
此外,数据也不会被无限、反复重发。达到一定重发次数之后,如果仍没有ACK返回,就会判断为网络或对端主机发生异常,强制关闭连接。并通知应用通信异常强行终止。
TCP以段为单位发送数据
在建立TCP连接同时,也可以确定发送数据包的单位,可以称之为最大消息长度(MSS)。
MSS是在三次握手时,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在TCP首部中写入MSS选项,告诉对方自己的接口能够适应MSS的大小(为了附加MSS选项,TCP首部将不再是20字节,而是4字节的整数倍)。然后在两者中选一个较小的值投入使用。
利用窗口控制提高速度
TCP以一个段为单位,每发送一个段进行一次确认应答的处理,缺点:包的往返时间越长通信性能就越低。
所以TCP引入窗口的概念,即使在往返时间较长的情况下,也能控制网络性能的下降。即发送端主机在发送了一个段后不必要一直等待确认应答,而是继续发送。
窗口大小就是指无需等待确认应答而可以继续发送数据的最大值,此处的窗口大小为4个段。
窗口机制使用了大量缓冲区(临时保存收发数据的场所。通常是在计算机内存中开辟的空间),对多个段同时进行确认应答的功能。
处于窗口内的数据即使没有收到ACK也可以发送出去。此外,如果数据丢失需要重发,那么发送端必须留着要重发的数据。因此,发送端主机在等到确认应答返回之前,必须在缓冲区中保留这部分数据。
收到确认应答的情况下,将窗口滑动到ACK中的序列号的位置。这样可以顺序地将多个段同时发送,提高通信性能。
窗口控制与重发控制
先考虑ACK未能返回的情况:数据已到达,是不需要进行重发的。然而,在没有窗口控制时,没收到ACK的数据都会被重发;使用了窗口控制。某些ACK即使丢失也无需重发:
其次考虑某个报文段丢失的情况:接收主机如果收到一个自己应该接受的序号以外的数据时,会针对当前位置收到数据返回ACK。(不过即使接收端主机收到的包序号不连续,也不会将数据丢弃,而是保存在缓冲区中)
此处为冗余ACK相关知识,看自顶向下即可。
流控制
TCP提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。具体操作是:接收端主机向发送端主机通知自己可以接受数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就是窗口大小。窗口大小是由接收端主机决定的。
TCP首部中专门有一个字段用来通知窗口大小。接收主机可以将自己可以接收的缓冲区大小放入该字段中通知发送端。该字段值越大,说明网络的吞吐量越高。
可以看出当接收端收到从3001号开始的数据段后其缓冲区即满,不得不暂时停止接收数据。之后在收到发送窗口更新通知后才得以继续进行。如果窗口的更新通知在传送途中丢失,可能会导致无法继续通信。为了避免该问题,发送端主机会时不时发送一个叫做窗口探测的数据段,此数据段仅含有一个字节以获取的最新的窗口大小信息。
拥塞控制
在网络出现拥堵时,如果突然发送一个较大量的数据,极有可能会导致整个网络的瘫痪。
TCP为防止该问题出现,在通信一开始会通过一个慢启动算法得出的数值,对发送数据量进行控制。
为了在发送端调节索要发送数据的量,定义一个叫做拥塞窗口(cwnd)的概念。于是在慢启动时,将这个拥塞窗口大小设置为1个数据段(1MSS)发送数据,之后每收到一次ACK,拥塞窗口的值就加1。在发送数据包时,将拥塞窗口的大小与接收端主机通知的窗口大小做比较,然后按照它们中较小的值,发送比其还要小的数据量。
如果重发采用超时机制,那么拥塞窗口的初始值可以设置为1以后再进行慢启动修正。有了上述机制,就可以减少通信开始时连续发包导致的网络拥堵。
发送方此时可以连续发送两个数据报文段,接收方收到该数据报文段后,给发送方一次发回2个确认报文段,发送方收到这两个确认报文后,将拥塞窗口的值加2变为4,发送方此时可连续发送4个报文段,接收方收到4个报文段后,给发送方依次回复4个确认报文,发送方收到确认报文后,将拥塞窗口加4,置为8,发送方此时可以连续发送8个数据报文段,接收方收到该8个数据报文段后,给发送方一次发回8个确认报文段,发送方收到这8个确认报文后,将拥塞窗口的值加8变为16
随着包的每次往返,拥塞窗口也会以1、2、4等指数函数的增长,拥堵状况激增甚至导致网络拥塞的发生。为了防止这种情况,引入了慢启动阈值的概念:只要拥塞窗口的值超过了该阈值,在每收到一次ACK时只允许以以下免这种比例放大拥塞窗口:
TCP通信开始时,没有设置相应的慢启动阈值。而是在超时重发时,才会设置为当时拥塞窗口一半的大小。
由重复确认应答进行高速重发控制时,慢启动阈值大小被设置为当时窗口大小的一半。(实际上是设置为实际已发送但未收到ACK的数据量的一半)。随后,将窗口大小设置为该慢启动阈值+3数据段的大小。
6.6 UDP首部的格式
除去数据的部分就是UDP的首部,UDP首部由源端口号、目标端口号、包长和校验和组成:
- 源端口号
表示发送端端口号,16位。该字段是可选项,有时可能不设置源端口号。没有源端口号的时候该字段值设置为0。可用于不需要返回的通信中。
- 目标端口号
表示接收端端口号,16位。
- 包长度
该字段保存了UDP首部的长度与数据的长度之和,8位。注意TCP和UDP的包长度是**首部+数据**
,而IP的包长度就是**首部**
。
- 校验和
校验和为了提供可靠的UDP首部和数据。
接收主机在接收到UDP数据报后,从IP首部获知IP地址信息构造UDP伪首部,再进行校验和计算。
6.7 TCP首部格式
TCP中没有表示包长度和数据长度的字段。可由IP层获知TCP的包长,由TCP包长可知数据的长度。
- 源端口号
表示发送端端口号,17位。
- 目标端口号
表示接收端端口号,16位。
- 序列号(Sequence Number)
32位。指发送数据的位置(序号)。每发送一次数据,就累加一次该数据字节量的大小。
序列号不会从0或1开始,而是在建立连接时由计算机生成的随机数作为初值,通过SYN包传给接收端主机。此外,在建立连接和断开连接时发送的SYN包和FIN包虽然不携带数据,但也会作为一个字节增加对应的序列号。
- 确认应答号(ACK)
32位。指下一次应该收到的数据的序列号。实际上是指已收到确认应答号-1为止的数据。发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。
- 数据偏移(首部长度)
该字段表示TCP所传输的数据部分应该从TCP包的哪个位开始计算,也可以看作TCP首部的长度。
该字段长4位,单位为4字节(32位)。不包含选项字段的话,如上图所示TCP首部为20字节长,因此数据偏移字段可以设置为5。意即,如果该字段值为5,说明TCP包最开始到20字节为止都是TCP首部,剩下的部分是TCP数据。
- 控制位
8位。其各自对应的含义如下:
CWR | 该标志与后面的ECE标志都用于IP首部的ECN字段。ECE标志为1时,则通知对方已将拥塞窗口缩小。 |
---|---|
ECE | 为1时会通知对方,从对方到这边的网络有拥塞。在收到数据报的IP首部中ECN字段为1时将TCP首部中的ECE设置为1。 |
URG | 为1时,表示包中有需要紧急处理的数据。 |
ACK | 为1时,确认应答的字段变为有效。TCP规定除了建立连接时的SYN包之外,该位必须为1。 |
PSH | 为1时,表示需要将收到的数据立刻传给上层应用协议;为0时,则不需要立刻传,而是先进行缓存。 |
RST | 为1时,表示TCP连接中出现异常必须强制断开连接。 |
SYN | 用于建立连接。为1表示希望建立连接。 |
FIN | 为1时,表示今后不会再有数据发送,希望断开连接。 |
- 窗口大小
16位。用于通知从相同TCP首部的确认应答号所指位置开始能够接收的数据大小(8位)。如果窗口为0,表示可以发送窗口探测,以了解最新的窗口大小。
- 校验和
感觉有点傻逼
- 紧急指针
16位。只有在URG控制位为1时有效。表示本报文段中紧急数据的指针。一般来说,从数据部分的收尾到紧急指针所指示的位置为止是紧急数据。因此也可以说是紧急指针指出了紧急数据的末尾在报文段中的位置。
如果处理紧急数据属于应用的问题。一般在暂时中断通信,或中断通信的情况下使用。
e.g. 在Web浏览器中点击停止按钮,或使用 Telnet 输入Ctrl+C
时都会有URG为1的包。
此外,紧急指针也用作表示数据流分段的标志。
- 选项(options)