UDP
UDP(User Data Protocol,用户数据报协议)它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如,在默认状态下,一次“ping”操作发送4个数据包。发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。
我们来看一下 UDP 的包头
由上图可以看出,UDP 除了端口号,基本啥都没有了。如果没有这两个端口号,数据就不知道该发给哪个应用。
所以 UDP 特别简单,有如下三个特点
- 沟通简单,不需要大量的数据结构,处理逻辑和包头字段
- 轻信他人。它不会建立连接,但是会监听这个地方,谁都可以传给它数据,它也可以传给任何人数据,甚至可以同时传给多个人数据。
- 愣头青,做事不懂变通。不会根据网络的情况进行拥塞控制,无论是否丢包,它该怎么发还是怎么发
因为 UDP 是”小孩子”,所以处理的是一些没那么难的项目,并且就算失败的也能接收。基于这些特点的话,UDP 可以使用在如下场景中
- 需要资源少,网络情况稳定的内网,或者对于丢包不敏感的应用,比如 DHCP 就是基于 UDP 协议的。
- 不需要一对一沟通,建立连接,而是可以广播的应用。因为它不面向连接,所以可以做到一对多,承担广播或者多播的协议。
- 需要处理速度快,可以容忍丢包,但是即使网络拥塞,也毫不退缩,一往无前的时候
UDP 的几个例子
- 直播。直播对实时性的要求比较高,宁可丢包,也不要卡顿的,所以很多直播应用都基于 UDP 实现了自己的视频传输协议
- 实时游戏。游戏的特点也是实时性比较高,在这种情况下,采用自定义的可靠的 UDP 协议,自定义重传策略,能够把产生的延迟降到最低,减少网络问题对游戏造成的影响
- 物联网。一方面,物联网领域中断资源少,很可能只是个很小的嵌入式系统,而维护 TCP 协议的代价太大了;另一方面,物联网对实时性的要求也特别高。比如 Google 旗下的 Nest 简历 Thread Group,推出了物联网通信协议 Thread,就是基于 UDP 协议的
UDP的优点
快,比TCP稍安全
UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,udp是一个无状态的传输协议,所以他在传输数据时非常快。M没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。UDP也是无法避免攻击的,比如:UDP flood攻击。。。
UDP的缺点
不可靠,不稳定
因为UDP没有TCP的那些可靠机制,在网络质量不好时很容易丢包。
TCP
TCP(Transmission Control Protocol,传输控制协议)
首先是 TCP 的包头格式
- 首先,源端口和目标端口是不可少的。
- 接下来是包的序号。主要是为了解决乱序问题。不编好号怎么知道哪个先来,哪个后到
- 确认序号。发出去的包应该有确认,这样能知道对方是否收到,如果没收到就应该重新发送,这个解决的是不丢包的问题
- 状态位。SYN 是发起一个链接,ACK 是回复,RST 是重新连接,FIN 是结束连接。因为 TCP 是面向连接的,因此需要双方维护连接的状态,这些状态位的包会引起双方的状态变更
窗口大小,TCP 要做流量控制,需要通信双方各声明一个窗口,标识自己当前的处理能力。
通过对 TCP 头的解析,我们知道要掌握 TCP 协议,应该重点关注以下问题:
- 顺序问题
- 丢包问题
- 连接维护
- 流量控制
- 拥塞控制
TCP 的三次握手
所有的问题,首先都要建立连接,所以首先是连接维护的问题
TCP 的建立连接称为三次握手,可以简单理解为下面这种情况
A:您好,我是 A
B:您好 A,我是 B
A:您好 B
对于 A 来说它发出请求,并收到了 B 的响应,对于 B 来说它响应了 A 的请求,并且也接收到了响应。
TCP 的三次握手除了建立连接外,主要还是为了沟通 TCP 包的序号问题。
A 告诉 B,我发起的包的序号是从哪个号开始的,B 同样也告诉 A,B 发起的 包的序号是从哪个号开始的。
双方建立连接之后需要共同维护一个状态机,在建立连接的过程中,双方的状态变化时序图如下所示
这是网上经常见到的一张图,刚开始的时候,客户端和服务器都处于 CLOSED 状态,先是服务端主动监听某个端口,处于 LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端接收了发起的连接,返回 SYN,并且 ACK ( 确认 ) 客户端的 SYN,之后处于 SYN-SENT 状态。客户端接收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后就处于 ESTAVLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,也处于 ESTABLISHED 状态,因为它也一发一收了。
TCP 四次挥手
说完建立连接,再说下断开连接,也被称为四次挥手,可以简单理解如下
A:B 啊,我不想玩了
B:哦,你不想玩了啊,我知道了
这个时候,只是 A 不想玩了,即不再发送数据,但是 B 可能还有未发送完的数据,所以需要等待 B 也主动关闭。
B:A 啊,好吧,我也不玩了,拜拜
A:好的,拜拜
这样整个连接就关闭了,当然上面只是正常的状态,也有些非正常的状态(比如 A 说完不玩了,直接跑路,B 发起的结束得不到 A 的回答,不知道该怎么办或则 B 直接跑路 A 不知道该怎么办),TCP 协议专门设计了几个状态来处理这些非正常状态
断开的时候,当 A 说不玩了,就进入 FIN_WAIT_1 的状态,B 收到 A 不玩了的消息后,进入 CLOSE_WAIT 的状态。
A 收到 B 说知道了,就进入 FIN_WAIT_2 的状态,如果 B 直接跑路,则 A 永远处与这个状态。TCP 协议里面并没有对这个状态的处理,但 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。
如果 B 没有跑路,A 接收到 B 的不玩了请求之后,从 FIN_WAIT_2 状态结束,按说 A 可以跑路了,但是如果 B 没有接收到 A 跑路的 ACK 呢,就再也接收不到了,所以这时候 A 需要等待一段时间,因为如果 B 没接收到 A 的 ACK 的话会重新发送给 A,所以 A 的等待时间需要足够长。
累计确认
TCP 如何实现可靠传输?
首先为了保证顺序性,每个包都有一个 ID。在建立连接的时候会商定起始 ID 是什么,然后按照 ID 一个个发送,为了保证不丢包,需要对发送的包都要进行应答,当然,这个应答不是一个一个来的,而是会应答某个之前的 ID,表示都收到了,这种模式成为累计应答或累计确认。
为了记录所有发送的包和接收的包,TCP 需要发送端和接收端分别来缓存这些记录,发送端的缓存里是按照包的 ID 一个个排列,根据处理的情况分成四个部分
- 发送并且确认的
- 发送尚未确认的
- 没有发送等待发送的
- 没有发送并且暂时不会发送的
这里的第三部分和第四部分就属于流量控制的内容
在 TCP 里,接收端会给发送端报一个窗口大小,叫 Advertised window。这个窗口应该等于上面的第二部分加上第三部分,超过这个窗口,接收端做不过来,就不能发送了
于是,发送端要保持下面的数据结构
对于接收端来讲,它的缓存里面的内容要简单一些
- 接收并且确认过的
- 还没接收,但是马上就能接收的
- 还没接收,但也无法接收的
对应的数据结构如下
顺序问题和丢包问题
结合上面的图看,在发送端,1、2、3 已发送并确认;4、5、6、7、8、9 都是发送了还没确认;10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的。
在接收端来看,1、2、3、4、5 是已经完成 ACK 但是还没读取的;6、7 是等待接收的;8、9 是已经接收还没有 ACK 的。
发送端和接收端当前的状态如下:
- 1、2、3 没有问题,双方达成了一致
- 4、5 接收方说 ACK 了,但是发送方还没收到
- 6、7、8、9 肯定都发了,但是 8、9 已经到了,6、7 没到,出现了乱序,缓存着但是没办法 ACK。
根据这个例子可以知道顺序问题和丢包问题都有可能存在,所以我们先来看确认与重传机制。
假设 4 的确认收到了,5 的 ACK 丢了,6、7 的数据包丢了,该怎么办?
一种方法是超时重试,即对每一个发送了但是没有 ACK 的包设定一个定时器,超过了一定的事件就重新尝试。这个时间必须大于往返时间,但也不宜过长,否则超时时间变长,访问就变慢了。
如果过一段时间,5、6、7 都超时了就会重新发送。接收方发现 5 原来接收过,于是丢弃 5;6 收到了,发送 ACK,要求下一个是 7,7 不幸又丢了。当 7 再次超时的时候,TCP 的策略是超时间隔加倍。每当遇到一次超时重传的时候,都会讲下一次超时时间间隔设为先前值的两倍。
超时重传的机制是超时周期可能相对较长,是否有更快的方式呢?
有一个快速重传的机制,即当接收方接收到一个序号大于期望的报文段时,就检测到了数据流之间的间隔,于是发送三个冗余的 ACK,客户端接收到之后,知道数据报丢失,于是重传丢失的报文段。
例如,接收方发现 6、8、9 都接收了,但是 7 没来,所以肯定丢了,于是发送三个 6 的 ACK,要求下一个是 7。客户端接收到 3 个,就会发现 7 的确又丢了,不等超时,马上重发。
流量控制的问题
在流量控制的机制里面,在对于包的确认中,会携带一个窗口的大小
简单的说一下就是接收端在发送 ACK 的时候会带上缓冲区的窗口大小,但是一般在窗口达到一定大小才会更新窗口,因为每次都更新的话,刚空下来就又被填满了
拥塞控制的问题
也是通过窗口的大小来控制的,但是检测网络满不满是个挺难的事情,所以 TCP 发送包经常被比喻成往水管里灌水,所以拥塞控制就是在不堵塞,不丢包的情况下尽可能的发挥带宽。
水管有粗细,网络有带宽,即每秒钟能发送多少数据;水管有长度,端到端有时延。理想状态下,水管里面的水 = 水管粗细 * 水管长度。对于网络上,通道的容量 = 带宽 * 往返时延。
如果我们设置发送窗口,使得发送但未确认的包为通道的容量,就能撑满整个管道。
如图所示,假设往返时间为 8 秒,去 4 秒,回 4 秒,每秒发送一个包,已经过去了 8 秒,则 8 个包都发出去了,其中前四个已经到达接收端,但是 ACK 还没返回,不能算发送成功,5-8 后四个包还在路上,还没被接收,这个时候,管道正好撑满,在发送端,已发送未确认的 8 个包,正好等于带宽,也即每秒发送一个包,也即每秒发送一个包,乘以来回时间 8 秒。
如果在这个基础上调大窗口,使得单位时间可以发送更多的包,那么会出现接收端处理不过来,多出来的包会被丢弃,这个时候,我们可以增加一个缓存,但是缓存里面的包 4 秒内肯定达不到接收端课,它的缺点会增加时延,如果时延达到一定程度就会超时重传
TCP 拥塞控制主要来避免两种现象,包丢失和超时重传,一旦出现了这些现象说明发送的太快了,要慢一点。
具体的方法就是发送端慢启动,比如倒水,刚开始倒的很慢,渐渐变快。然后设置一个阈值,当超过这个值的时候就要慢下来
慢下来还是在增长,这时候就可能水满则溢,出现拥塞,需要降低倒水的速度,等水慢慢渗下去。
拥塞的一种表现是丢包,需要超时重传,这个时候,采用快速重传算法,将当前速度变为一半。所以速度还是在比较高的值,也没有一夜回到解放前。
Tcp的优点
可靠,稳定
TCP的可靠性体现在传输数据之前,三次握手建立连接(四次挥手释放连接),并且在数据传递时,有确认、窗口、重传、拥塞控制机制,数据传完之后,断开连接用来节省系统资源。
TCP的缺点
慢,效率低,占用系统资源高,易被攻击
传数据之前建立连接,这样会消耗时间,而且在消息传递时,确认机制、重传机制和拥塞控制机制都会消耗大量的时间,而且要在每台设备上维护所有的传输连接。而每个连接都会占用系统的CPU、内存等硬件软件资源。并且TCP的取而机制、三次握手,这些也导致TCP容易被人利用,实现DOS,DDOS攻击。
TCP 和 UDP 的区别
UDP 是一种面向无连接,且不可靠的协议,在通信过程中,它并不像 TCP 那样需要先建立一个连接,只要(目的地址,端口号,源地址,端口号)确定了,就可以直接发送信息报文,并且不需要确保服务端一定能收到或收到完整的数据。它仅仅提供了校验和机制来保障一个报文是否完整,若校验失败,则直接丢弃报文,不做任何处理。
TCP和UDP都是传输层协议,但是两者具有不同的特性和应用场景
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
- UDP程序结构较简单
- TCP 是面向字节流的,UDP 是基于数据报的
- TCP 保证数据正确性,UDP 可能丢包
- TCP 保证数据顺序,UDP 不保证
- TCP要求系统资源较多,UDP较少。
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)。
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
- TCP首部开销20字节;UDP的首部开销小,只有8个字节。
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
名词解释
面向报文和面向字节流
面向报文的传输方式是应用层交给UDP多长的报文,UDP就发送多长的报文,即一次发送一个报文。因此应用程序必须选择大小合适的报文。报文太长,则IP层需要分片,降低效率。
面向字节流的传输方式是应用程序和TCP的交互是一次一个数据块(大小不等),TCP把这些数据块看成是一连串无结构的字节流。TCP有一个缓冲,当应用程序传送的数据块太长,TCP就会把它分割成多块传送。
双工性
全双工:是指在发送数据的同时也能够接收数据,两者同步进行,这好像我们平时打电话一样,说话的同时也能够听到对方的声音。目前的网卡一般都支持全双工。
半双工:所谓半双工就是指一个时间段内只有一个动作发生,举个简单例子,一条窄窄的马路,同时只能有一辆车通过,当目前有两量车对开,这种情况下就只能一辆先过,等到头儿后另一辆再开,这个例子就形象的说明了半双工的原理。
TCP/UDP编程模型
从程序实现的角度来看,可以用下图来进行描述。
从上图也能清晰的看出,TCP通信需要服务器端侦听listen、接收客户端连接请求accept,等待客户端connect建立连接后才能进行数据包的收发(recv/send)工作。而UDP则服务器和客户端的概念不明显,服务器端即接收端需要绑定端口,等待客户端的数据的到来。后续便可以进行数据的收发(recvfrom/sendto)工作。
前面提到了UDP保留了报文的边界,下面我们来谈谈TCP和UDP中报文的边界问题。在默认的阻塞模式下,TCP无边界,UDP有边界。
对于TCP协议,客户端连续发送数据,只要服务端的这个函数的缓冲区足够大,会一次性接收过来,即客户端是分好几次发过来,是有边界的,而服务端却一次性接收过来,所以证明是无边界的;
而对于UDP协议,客户端连续发送数据,即使服务端的这个函数的缓冲区足够大,也只会一次一次的接收,发送多少次接收多少次,即客户端分几次发送过来,服务端就必须按几次接收,从而证明,这种UDP的通讯模式是有边界的。
TCP 与 UDP 的应用场景
从特点上我们已经知道,TCP 是可靠的但传输速度慢 ,UDP 是不可靠的但传输速度快。因此在选用具体协议通信时,应该根据通信数据的要求而决定。
若通信数据完整性需让位与通信实时性,则应该选用 TCP 协议(如文件传输、重要状态的更新等);反之,则使用 UDP 协议(如视频传输、实时通信等)。
什么时候应该使用TCP
当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。
在日常生活中,常见使用TCP协议的应用如下:
- 浏览器,用的HTTP
- FlashFXP,用的FTP
- Outlook,用的POP、SMTP
- Putty,用的Telnet、SSH
- QQ文件传输
什么时候应该使用UDP
当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。
比如,日常生活中,常见使用UDP协议的应用如下:
- QQ语音
- QQ视频
- TFTP
总结
什么是面向连接,什么是面向无连接
在互通之前,面向连接的协议会先建立连接,如 TCP 有三次握手,而 UDP 不会
TCP 为什么是可靠连接
- 通过 TCP 连接传输的数据无差错,不丢失,不重复,且按顺序到达。
- TCP 报文头里面的序号能使 TCP 的数据按序到达
- 报文头里面的确认序号能保证不丢包,累计确认及超时重传机制
- TCP 拥有流量控制及拥塞控制的机制
TCP 的顺序问题,丢包问题,流量控制都是通过滑动窗口来解决的
拥塞控制时通过拥塞窗口来解决的
两种协议都是传输层协议,为应用层提供信息载体。TCP协议是基于连接的可靠协议,有流量控制和差错控制,也正因为有可靠性的保证和控制手段,所以传输效率比UDP低;UDP协议是基于无连接的不可靠协议,没有控制手段,仅仅是将数据发送给对方,因此效率比TCP要高。
基于上述特性,不难得到结论,TCP协议适用于对效率要求相对低,但对准确性要求相对高的场景下,或者是有一种连接概念的场景下;而UDP协议适用于对效率要求相对高,对准确性要求相对低的场景。
几个应用的例子。TCP一般用于文件传输(FTP HTTP 对数据准确性要求高,速度可以相对慢),发送或接收邮件(POP IMAP SMTP 对数据准确性要求高,非紧急应用),远程登录(TELNET SSH 对数据准确性有一定要求,有连接的概念)等等;UDP一般用于即时通信(QQ聊天 对数据准确性和丢包要求比较低,但速度必须快),在线视频(RTSP 速度一定要快,保证视频连续,但是偶尔花了一个图像帧,人们还是能接受的),网络语音电话(VoIP 语音数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题)等等。
一些其他应用。比如,TCP可以用于网络数据库,分布式高精度计算系统的数据传输;UDP可以用于服务系统内部之间的数据传输,因为数据可能比较多,内部系统局域网内的丢包错包率又很低,即便丢包,顶多是操作无效,这种情况下,UDP经常被使用。