TCP/IP 中有两个具有代表性的传输层协议,分别是TCP、UDP。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。
要知道TCP为了这简单描述“可靠的通信传输”背后所做的努力,你会深感佩服其强大性。TCP的特征:序列化+确认应答、超时重发、流量控制、拥塞控制等等,每一个都是为了能够可靠不丢包遗漏地将数据包传输给对方,而此篇文章将详细来解析TCP的这些精髓所在,涉及的知识点有:
- TCP概念、优缺点
- 序列化+确认应答、三次握手与四次挥手、超时重发、流量控制、拥塞控制
- TCP与UDP的区别
- 常见面试问题
- 端口号概念
一. TCP重点解析
TCP位于传输层,提供可靠的字节流服务(Byte Stream Service)。
- 字节流服务
为了方便传输,将大块数据分割以报文段(segment)为单位的数据包进行管理。应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序看成是一连串的无结构的字节流。
当数据块太长时,TCP中的缓存可以将应用程序发来的数据块划分成若干个短部分再传送。
当数据块太短时,例如应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去。
- 可靠的传输服务:能够把数据准确可靠地传给对方。
优点:
- 可靠、稳定性
- 传递数据前,会有三次握手建立连接 ;
- 传递数据时,有确认、窗口、重传、拥塞控制;
- 传递数据后,会断开连接节省系统资源;
缺点:
- 传输慢,效率低,占用系统资源高:
传递数据前,建立连接需要耗时
传递数据时,确认、重传、拥塞等会消耗大量时间以及CPU和内存等硬件资源
- 易被攻击 :因为有确认机制,三次握手等机制,容易被人利用,实现DOS 、DDOS攻击
TCP协议为了更容易传送大数据才把数据分割,而且TCP协议能够确认数据最终是否送达到对方。为了通过IP数据报实现可靠传输,需要考虑很多事情,例如数据破坏、丢包、重复以及分片顺序混乱等问题。若无法节解决,无需谈论可靠传输。
TCP通过检验和、序列号、确认应答、重发控制、连接管理及窗口等机制实现可靠传输。
1. 可靠传输(序列化+确认 应答)
(1)确认应答ACK(Positive Acknowledgment)
在TCP中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知。此消息叫做确认应答ACK。
如图1,TCP通过肯定的确认回答(ACK)实现可靠的数据传输。当发送端将数据发出后等待对方的确认应答,若有应答则表示对方已成功接收。反之则数据丢失可能性大。
如图2,在一定时间内没有等待确认应答,发送端就可以认为数据已丢失,进行重发。因此即使丢包,仍然可以保证数据传输到对方。(注意:未收到应答不一定是数据丢失,也可能是确认应答在途中丢失)
(2)序列号
此外,也有可能是其它原因导致发送端未收到确认应答,主机只需按照机制重发数据即可,但对于目标主机而言,它会收到相同的数据包,为此引入一种机制,能够识别是否已经接受数据和判断是否需要接收。
上述这些确认应答处理、重发控制以及重复控制等功能都可以通过序列号实现。序列号是按顺序给发送数据的每一个字节(8位字节)都标上号码的编号。接收端查询接收数据TCP首部中的序列号和数据长度,将下一步应接收的序号作为确认应答返送回去。
综上,通过序列号和确认应答,TCP可以实现可靠传输。
2. 重发超时
重发超时是指在重发数据之前,等待确认应答到来的那个特定时间间隔,如果超过这个时间仍未收到确认应答,发送端将进行数据重发。TCP要保证所有的数据包都可以到达,所以,必需要有重传机制。
接收端给发送端的Ack确认只会确认最后一个连续的包
几个例子,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办?
(1)超时重传机制
发送端继续等待3,即使收到了4,也不会回复ack。当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 4,意味着3和4都收到了。此机制下有两种应对方法:
仅重传timeout的包。也就是第3份数据。
重传timeout后所有的数据,也就是第3,4,5这三份数据
第一种会节省带宽,但是慢,第二种会快一点,但是会浪费带宽。但其实这两种方法都不太好,都需要等待timeout(timeout可能会很长)。
(2)快速重传(Fast Retransmit )机制
TCP引入了一种叫Fast Retransmit 的算法,不以时间驱动,而以数据驱动重传。如果数据包没有连续到达,就ack最后那个可能被丢了的包,如果发送端连续收到3次相同的ack就重传。其好处是发送端不需要一直等待,直到timeout后再重传。
如上图,再举个例子,如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。
虽然快速重传机制解决了timeout的问题,还遗留了之前提出的一个问题:是重传之前的一个还是重传所有数据包?对于上面的示例来说,是重传#2呢还是重传#2,#3,#4,#5呢?因为发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的。
因此,快速重传机制仍然是有缺点的!
(3)SACK 方法
Selective Acknowledgment (SACK)方法:需要在TCP头里加一个SACK的东西,ACK还是Fast Retransmit的ACK,SACK则是汇报收到的数据碎版。参考下图:
在发送端就可以根据回传的SACK来知道哪些数据到了,哪些没有到。
但是!SACK会消费发送方的资源,试想,如果一个攻击者给数据发送方发一堆SACK的选项,这会导致发送方开始要重传甚至遍历已经发出的数据,这会消耗很多发送端的资源。
(4)Duplicate SACK – 重复收到数据的问题
主要使用了SACK来告诉发送方有哪些数据被重复接收了,D-SACK使用了SACK的第一个段来做标志:
- 如果SACK的第一个段的范围被ACK所覆盖,那么就是D-SACK
- 如果SACK的第一个段的范围被SACK的第二个段覆盖,那么就是D-SACK
D-SACK方法的好处:
- 可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢了。
- 是不是自己的timeout太小了,导致重传。
- 网络上出现了先发的包后到的情况(又称reordering)
- 网络上是不是把我的数据包给复制了。
3. 连接管理
为了准确无误地将数据送达目标处,TCP协议采用了三次握手(three-way handshaking)策略。用TCP协议将数据包送出去后,TCP一定会向对方确认是否成功送达。若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的顺序包。
(1)三次握手过程
定义:三次握手,是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。
目的:是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息
在socket编程中,客户端执行connect()时,将触发三次握手。
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据。
(2)四次挥手
所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:
由于TCP连接时全双工的,因此每个方向都必须要单独进行关闭。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。
- 第一次挥手:主动关闭方发送一个FIN并进入FIN_WAIT1状态
- 第二次挥手:被动关闭方接收到主动关闭方发送的FIN并发送ACK,此时被动关闭方进入CLOSE_WAIT状态;主动关闭方收到被动关闭方的ACK后,进入FIN_WAIT2状态
- 第三次挥手:被动关闭方发送一个FIN并进入LAST_ACK状态
- 第四次挥手:主动关闭方收到被动关闭方发送的FIN并发送ACK,此时主动关闭方进入TIME_WAIT状态,经过2MSL时间后关闭连接;被动关闭方收到主动关闭方的ACK后,关闭连接
(3)为什么建立连接是三次握手,而关闭连接却是四次挥手呢
对于3次握手:主要是要初始化Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number)——所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的 x 和 y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。
对于4次挥手:其实你仔细看是2次,因为TCP是全双工的,所以,发送方和接收方都需要Fin和Ack。只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到CLOSING状态,然后到达TIME_WAIT状态。
服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
二. 流控制(Flow Control)
1. 滑动窗口协议
此部分进行的讲解重心还是放在网络传输的可靠性,仔细探究TCP协议是如何解决网络传输的不可靠问题,其中有个非常关键部分——滑动窗口协议,同时可验证网络学科的实践性、工程性。
(1)滑动窗口协议的特征定义
- TCP协议中使用
- 维持发送方/接收方缓存区
此缓存区主要用于解决网络传输的不可靠问题,在上一点已经介绍过网络传输的不可靠问题,如丢包、重复包、出错等,在TCP协议中发送方、接收方各自维护自己的缓存区,互相商定包的重传机制,由此解决不可靠问题。
(2)提出问题
如果没有滑动窗口协议,如何保证接收方能够收到正确有序的包?
如上图所示,发送方发送包1,接收方确认包1,发送包2,确认包2,这样即可解决不可靠性问题。但同时此过程的问题十分明显:吞吐量低,必须要等接收方确认完后才能发送下一个包。试考虑,若能连发几个包,接收方可以同时确认,这样效率岂不更高?
(3)简单改进
在此问题上,出现了以下改进:发送方可以同时发多个包,接收方一起确认。
(4)深度改进——滑动窗口实现
由此又衍生出一个问题,同时发包的数量多少才会是最优方案呢?例如发送方同时发送包1、2,在获得接收方确认包1消息后,能否不等包2确认信息,直接发送包3呢?
这样很自然地思考到了“滑动窗口实现”。以下有16个数据包,发送方希望按照顺序发送,在接收方收到每个包后都逐一给予确认:
初始:(窗口为4到7)
- 1、2、3包已发送并且获取发送方Ack确认;
- 4、5、6、7包已发送但尚未获取发送方Ack确认;
- 8、9、10包待发送;
- 而11、12、13、14、15、16包未发送甚至都没有装入内存;
正常:(窗口为5到9)
- 1、2、3、4包已发送并且获取发送方Ack确认;
- 5、6、7、8、9包已发送但尚未获取发送方Ack确认;
- 10、11包待发送;
- 而12、13、14、15、16包未发送甚至都没有装入内存;
丢Ack:(窗口为5到11)
5、6、7、8、9包未收到Ack(丢Ack),在等待过程又发送了10、11包,此时窗口已满,无法读进包12,只能等待Ack。如果真的是丢包,始终无法收到Ack,此时超时重传机制会从包5开始重新发送。(注意,这里的Ack是按照顺序发送的!)
重发: (窗口为9到15)
- 5、6、7、8包获取发送方Ack确认;
- 9、10、11、12、13包已发送但尚未获取发送方Ack确认;
- 13、14包待发送;
- 而16包未发送甚至都没有装入内存;
(5)总结
运用工程学的思想来考虑滑动窗口机制较为容易,为了增加线路的吞吐量,改进原版方案,令发送方同时发送包;为了衡量同时发送的数量达到吞吐量最优解,从而引进滑动窗口机制;为了解决丢包等不可靠性问题导致发送方无法收到接收方的Ack,又引进了重发机制。以上一系列过程使得整个传输过程更加可靠。
2. 解释流控制
理解以上滑动窗口协议的示例后,大体也就知道了“流控制”是怎么回事儿,此部分用较书面的表达再解释一次。
(1)出现的问题
发送端根据自己的实际情况发送数据,但是接收端在处理别的事(可能正处于高负荷的状态无法接收任何数据),而且此数据包并无重要意义,这样导致此包丢失又会触发重发机制,令网络流量无端浪费。
(2)解决方法
为了防止此现象发生,TCP提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量。这就是“控制流”。
(3)具体操作
接收端想发送端通知自己可以接收数据的大小,发送端发送时不会超过这个限度的数据,该大小限制被称为窗口大小。
TCP首部中专门有一个字段用来通知窗口大小。接收端将自己可接收的缓冲区大小放入字段中并通知发送端。此值越大代表网络的吞吐量越高。
当缓存区一旦面临数据溢出时,窗口大小的值也会随之被设置成一个更小的值发送给发送端,从而控制数据发送量。也就是说,发送端会根据接收端的指示,对发送数据量进行控制。这就形成了一个完整的TCP流控制。
查看下图示例:
如果此窗口更新通知在传送途中丢失,可能导致无法继续通信。为避免此类问题产生,发送端主机会时不时发送一个叫做“窗口探测”的数据段,此数据段仅含一个字节以获取最新的窗口大小信息。
3. Zero Window
通过以上讲解后,可知发送端可以发送数据动态修改“滑动窗口”的大小,注意其值是可以为0的!那这样是否意味着发送端就不发数据了?确实如此,接收端都已经表示自己无力接收了,因此不会再发,类似于“Window Closed”。
可是过了一会儿,接收端如何通知发送端窗口可用了呢?
解决这个问题,TCP使用了Zero Window Probe技术,缩写为ZWP。即发送端在窗口变成0后,会发ZWP的包给接收端,让接收端来ack他的Window尺寸,一般这个值会设置成3次,第次大约30-60秒(不同的实现可能会不一样)。如果3次过后还是0的话,有的TCP实现就会发RST把链接断了。
注意:只要有等待的地方都可能出现DDoS攻击,Zero Window也不例外,一些攻击者会在和HTTP建好链发完GET请求后,就把Window设置为0,然后服务端就只能等待进行ZWP,于是攻击者会并发大量的这样的请求,把服务器端的资源耗尽。
三. 拥塞控制(Congestion Control)
有了TCP的窗口控制,收发端之间不再以一个数据段为单位发送确认应答,也能够连续发送大量数据包。但是刚开始通信就发送大量数据会引发其它问题。计算机网络处于一个共享的环境,因此有可能因为其他主机之间的通信使得网络拥堵,此时突然发送一个大量数据,可能导致整个网络瘫痪。
拥塞控制主要是四个算法:
- 慢启动
- 拥塞避免
- 拥塞发生
- 快速恢复
1. 慢启动(Slow Start)
首先,为了在发送端调节待发送的数据量,定义了“拥塞窗口”的概念,在慢启动时将其设为1个数据段(IMSS)发送数据,之后每收到一次确认应答(ACK),拥塞窗口的值就加1。在发送数据段时,将拥塞窗口的大小与接收端通知的窗口大小做比较,以较小值为标准,发送比其还要小的数据量。
根据以上机制,可有效减少通信开始时连续发包导致的网络拥堵,还可以避免网络拥塞的情况。
慢启动的算法如下:(cwnd全称Congestion Window)
- 连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
- 每当收到一个ACK,cwnd++; 呈线性上升
- 每当过了一个RTT,cwnd = cwnd*2; 呈指数让升
- 还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)
所以,我们可以看到,如果网速很快的话,ACK也会返回得快,RTT也会短,那么,这个慢启动就一点也不慢。下图说明了这个过程
2. 拥塞避免算法(Congestion Avoidance)
前面慢启动的算法第四步中的ssthresh(slow start threshold)是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。一般来说ssthresh的值是65535,单位是字节,当cwnd达到这个值时后,算法如下:
- 收到一个ACK时,cwnd = cwnd + 1/cwnd
- 当每过一个RTT时,cwnd = cwnd + 1
这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。很明显,是一个线性上升的算法。
3. 拥塞状态时的算法
在讲解超时重传机制中提到TCP面临丢包时,有以下两个问题:
a)等到RTO 超时,重传数据包。TCP认为这种情况太糟糕,反应也很强烈:
b) Fast Retransmit算法,也就是在收到3个duplicate ACK时就开启重传,而不用等到RTO超时,TCP Tahoe的实现和RTO超时一样。TCP Reno的实现是:
上面我们可以看到RTO超时后,sshthresh会变成cwnd的一半,这意味着,如果cwnd<=sshthresh时出现的丢包,那么TCP的sshthresh就会减了一半,然后等cwnd又很快地以指数级增涨爬到这个地方时,就会成慢慢的线性增涨。我们可以看到,TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。
4. 快速恢复算法(Fast Recovery)
快速重传和快速恢复算法一般同时使用。快速恢复算法是认为,你还有3个Duplicated Acks说明网络也不那么糟糕,所以没有必要像RTO超时那么强烈。 注意,正如前面所说,进入Fast Recovery之前,cwnd 和 sshthresh已被更新:
真正的Fast Recovery算法如下:
- cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
- 重传Duplicated ACKs指定的数据包
- 如果再收到 duplicated Acks,那么cwnd = cwnd +1
- 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
仔细思考一下你会发现上面这个算法也有问题:它依赖于3个重复的Acks。
注意:3个重复的Acks并不代表只丢了一个数据包,很有可能不止一个。但此算法只会重传一个,而剩下的那些包只能等到RTO超时,从而导致一种可怕的现象:超时一个窗口就减半一下,多个超时会超成TCP的传输速度呈级数下降,而且也不会触发Fast Recovery算法了。
(还有一些其他算法,在此只着重选取几个重要的介绍,其余的读者可自行了解学习)
四. TCP和UDP的区别
1. UDP(User Datagram Protocol)
用户数据包协议,UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。即使是出现网络拥堵的情况下,UDP也无法进行流量控制等避免网络拥塞的行为,同样出现丢包情况,UDP也不负责重发,也没有当包的到达顺序混乱纠正功能。如果需要这些细节控制,只能交由UDP的应用程序去处理。(UDP有点类似于用户说啥就听啥的机制)
面向报文传输,应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。因此,应用程序必须选择合适大小的报文。
- 若报文太长,则IP层需要分片,降低效率。
- 若报文太短,浪费资源。
UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。即应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。
因此它有以下特点:
- 传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快的把它扔到网络上
- 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息
- 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制
- 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段
- UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小
- 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制
- UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
优点
- 传输速率快:传输数据前,不需要像TCP一样建立连接;传输数据时,没有确认、窗口、重传、拥塞控制等机制。
- 较安全:由于没有了TCP的一些机制,被攻击者利用的漏洞就少了
缺点
不可靠,不稳定:由于没有了TCP的机制,在数据传输时如果网络不好,很可能丢包
2. 区别
其实在真正理解了TCP相关特征属性后,两者的区别显而易见就出来了,简单的总结如下:
TCP | UDP |
面向字节流 | 面向报文 |
一对一 | 可以一对一,一对多 |
面向有链接的通信服务 | 面向无连接的通信服务 |
速度快 | 速度慢 |
提供可靠的通信传输 | 不可靠,会丢包 |
保证数据包顺序 | 不保证 |
有流量控制,拥塞控制 | 没有 |
数据无边界 | 数据有边界 |
报头至少20字节 | 报头8字节 |
3. 常见问题
(1)为什么UDP比TCP快
因为TCP中连接需要三次握手,断开连接需要四次握手,传输过程中还有拥塞控制,控制流量等机制。
(2)为什么TCP比UDP可靠
TCP是面向有连接的,建立连接之后才发送数据;而UDP则不管对方存不存在都会发送数据。
TCP有确认机制,接收端每收到一个正确包都会回应给发送端。超时或者数据包不完整的话发送端会重传。UDP没有。因此可能丢包。
(3)什么时候使用TCP
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。
例如日常生活中使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输
(4)什么时候应该使用UDP
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。
比如日常生活中使用UDP协议的应用如下: QQ语音 QQ视频 TFTP
(5)TCP无边界,UDP有边界
- TCP无边界:客户端分多次发送数据给服务器,若服务器的缓冲区够大,那么服务器端会在客户端发送完之后一次性接收过来,所以是无边界的。
- UDP有边界:客户端每发送一次,服务器端就会接收一次,也就是说发送多少次就会接收多少次,因此是有边界的。
五. 面试问题
1. 在浏览器输入一个URL按下回车后,其流程是?
分析:如果要连接到远程服务器,首先要知道服务器的IP地址和端口,然后发送请求到服务器,服务器再响应。因此寻址和如何建立连接是关键。
步骤:
(1)进行寻址:若浏览器缓存中存有URL的对应IP,则直接查询IP;否则访问DNS(Domain Name System)进行寻址(Domain Name Resolution)。
(2) DNS或者URL Cache返回网页服务器的IP地址。
(3)浏览器与网页服务器进行三次握手建立TCP连接。由于是网页浏览服务,故连接到服务器的80端口。
(4)浏览器与服务器建立HTTP会话(Session),接收来自服务器的HTTP数据。
(5)浏览器解析HTTP数据,在本地窗口内渲染并显示网页。
(6)当浏览器页面被关闭时,终止HTTP会话并关闭连接。
2. 设计一个可靠的UDP,如何做?
分析: “可靠”是指接收端能够将收到的数据情况反馈给发送端。因此完全可以参照可靠的传输协议—–TCP,引入ACK、Flow Cotrol、Congestion Control等模块。可靠的UDP核心在于反馈机制,以下是几种实现方式。
解答:(由于可靠性要求在接收端能够恢复数据包的顺序,故发送端每个数据包都需要序列号。这里着重讨论反馈机制)
(1)最朴素的ACK发送
发送端每发送一个数据包,都需要接收端返回ACK,一旦超时,发送端重新发送数据包,直到该数据包被接收端ACK。该方法效率不高,因为之后的所以数据包都有可能被当前数据包block,并且每次返回ACK增加了overHead。
(2)Block/bit map ACK:
发送端发送一批数据包,例如32个,编号是0~31.接收端发回的ACK中用32bit(4byte)的bit map表示收到哪些数据包,发送端再一次性重发所有未被收到的数据包。该方法能够更加充分地利用带宽,在发送端一次性传输更多数据,但缺点是发送、接收端需要更深的buffer来暂存传输的所有数据。
(3)ACK last packet
发送端可以在发送最后一个数据包时要求接收端反馈ACK,并重发丢失的数据包。好处是可以减少由ACK造成的 data overhead,但需要buffer暂存数据。
最佳方法:
事实上,可以结合方法2、3,在每一批数据包的最后一个置位request ACK flag,要求接收端返回bit map ACK。更进一步,可以根据丢包率及延迟,估计网络状态,动态调整bit map大小:网络状态好时,用更大的bit map,即同时发送更多数据。否则减少发送数据量。这种对网络状况的自适应相当于实现了Congestion Control。
3. 实时视频会议应用应选择UDP还是TCP?
分析:本题的关键在于比较TCP和UDP的特点,并且根据此场景选择。
TCP的重传机制特点,会增加延迟,所以不适合此场景。其次视频音频编码本身可以容忍数据出错甚至数据丢失,因此并不需要TCP进行可靠传输。当某一视频帧出现丢包,可以直接跳过。一旦出现网络堵塞情况,发送端应主动丢弃一部分数据,因为即使被发送到接收端也都“过期”了,不会被解码显示。
不过即使采用UDP,也需要实现TCP某些模块,例如Flow Control、Congestion Control 来判断接收端的播放情况和网络情况,也需要反馈机制来判断接收端的接收状况。尽管当前场景不需要ACK每个数据包,但接收端可以反馈当前收到最新完整视频帧的序号,这样即使丢包,发送端可以接收端收到最新视频帧为基础,压缩后继的视频。
4. 网络中常见的ping 命令是什么协议?
分析使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。
书面解答: ping.exe的原理是向指定的IP地址发送一定长度的数据包,若指定IP地址存在会返回同样大小的数据包,若在特定时间内没有返回,就是“超时”,即指定IP地址不存在。由于ping使用的ICMP协议,有些防火墙软件会屏蔽ICMP协议,所以ping结果只能作为参考,ping不通不一定代表对方IP不存在。
(ping命令是一个非常有用的网络命令,大家常用它来测试网络连通情况。但同时它也是一把“双刃剑”,别人使用ping命令能探测到你计算机上的很多敏感信息,造成不安全。为了安全,防止ping的方法也有很多,比如防火墙,又比如创建一个禁止所有计算机ping本机IP地址的安全策略。)
ICMP协议: ICMP是“Internet Control Message Protocol”(Internet控制消息协议)的缩写。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
5. 如何编写Socket套接字?(扩展)
分析:Socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了。其定义类似于文件句柄的定义。
- 如果你要编写的是一个服务程序,那么先调用socket()创建一个套接字,调用bind()绑定IP地址和端口,然后启动一个死循环,循环中调用accept()接受连接。对于每个接受的连接,可以启动多线程方式进行处理,在线程中调用send()、recv()发送和接收数据。
- 如果你要编写的是一个客户端程序,那么就简单多了。先调用socket()创建一个套接字,然后调用connect()连接服务器,之后就是调用send()、recv()发送和接收数据了。
解答:
服务器端程序编写:
- 调用 ServerSocket(int port) 创建一个服务器端套接字,并绑定到指定端口上。
- 调用 accept(),监听连接请求,则接收连接,返回通信套接字。
- 调用Socket类的 getOutStream() 和 getInputStream()获取输出流和输入流,开始网络数据的发送和接收。
- 关闭通信套接字 Socket.close()。
客户端程序编写:
- 调用 Socket() 创建一个流套接字,并连接到服务器端。
- 调用Socket类的 getOutputStream()和 getInputStream()获取输出流和输入流,开始网络数据的发送和接收。
- 关闭通信套接字 Socket.close()。
6. 端口概念(扩展)
在网络技术中,端口(Port)大致有两种意思:
- 一是物理意义上的端口,比如,ADSL MODEM、集线器、交换机、路由器用于连接其他网络设备的接口;
- 二是逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围为0~65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口。
这里将主要介绍逻辑意义上的端口,逻辑意义上的端口有多种分类标准,下面将介绍两种常见的分类:
(1)按端口号分布划分
- 知名端口(Well-Known Ports):即众所周知的端口号,范围为0~1023,这些端口号一般固定分配给一些服务。
比如21端口分配给FTP服务,25端口分配给SMTP(简单邮件传输协议)服务,80端口分配给HTTP服务。
- 动态端口(Dynamic Ports):范围为1024~65535,这些端口号一般不固定分配给某个服务,也就是说许多服务都可以使用这些端口。只要运行的程序向系统提出访问网络的申请,那么系统就可以从这些端口号中分配一个供该程序使用。比如1024端口就是分配给第一个向系统发出申请的程序。在关闭程序进程后,就会释放所占用的端口号。
(2)按协议类型划分
按协议类型划分,可以分为TCP、UDP、IP和ICMP(Internet控制消息协议)等端口。下面主要介绍TCP和UDP端口:
- TCP端口:即传输控制协议端口,需要在客户端和服务器之间建立连接,这样可以提供可靠的数据传输。常见的包括FTP服务的21端口,Telnet服务的23端口,SMTP服务的25端口,以及HTTP服务的80端口等等。
- UDP端口:即用户数据包协议端口,无须在客户端和服务器之间建立连接,安全性得不到保障。常见的有DNS服务的53端口,SNMP(简单网络管理协议)服务的161端口,QQ使用的8000和4000端口等等。
以上内容将重点主要放在TCP协议解析上,深究下来你会发现TCP协议并不简单,其中涉及到的特性和相关的算法比较多,此篇内容只是偏白话文的方式进行了总结,可能有许多地方还不够详细,所以还是推荐各位阅读TCP的大佬之作《详解TCP/IP》,更为权威,其中的讲解更加学术深入。
共勉~