为什么DNS使用UDP而不是TCP

DNS在进行区域传输的时候使用TCP,普通的查询使用UDP。为什么查询是使用UDP呢?网络上大部分答案都说UDP性能更好,打开网页速度快。如果是这样的话,为什么HTTP却是使用TCP呢?
https://mp.weixin.qq.com/s/BF0EOyN2PtqN9ec0oyYS8g

DNS在进行区域传输的时候使用TCP,普通的查询使用UDP。为什么查询是使用UDP呢?网络上大部分答案都说UDP性能更好,打开网页速度快。如果是这样的话,为什么HTTP却是使用TCP呢?



正文



衡量计算机通信快慢的指标是“响应时间”,即从用户发出通信指令(输入网址敲回车键)开始,到用户看到完整页面为止,所流逝的时间。



响应时间(ResponseTime)



以浏览器为例,这个响应时间大体分为三部分:



响应时间= DNS域名解析时间+ TCP 连接建立时间 + HTTP交易时间



如果让响应时间尽可能小,只有让等号右侧的三者尽可能小。



TCP连接是固定的三次握手,所以很难有进一步缩小的空间。



HTTP交易,基于Request / Response,也很难有提升的空间。



所以,只能让DNS域名解析的时间越小越好。



域名解析



采用TCP传输,则域名解析时间为:



DNS域名解析时间 = TCP连接时间 + DNS交易时间



采用UDP传输,则域名解析时间为:



DNS域名解析时间 = DNS交易时间



很显然,采用UDP传输,DNS域名解析时间更小。



不就多一次TCP连接时间吗?



NO!



在很多时候,用户在访问一些冷门网站时,由于DNS服务器没有冷门网站的解析缓存,需要到域名根服务器、一级域名服务器、二级域名服务器迭代查询,直到查询到冷门网站的权威服务器,这中间可能涉及到多次的查询。



如果使用TCP传输,多几次查询,就多几次TCP连接时间,这多出来的时间不容小觑。



UDP传输的弱点



由于历史的原因,互联网上物理链路的最小MTU = 576,基于UDP传输的DNS为了限制报文不超过576,所以将DNS报文限制在512字节。



这样一旦DNS查询应答超过512字节,基于UDP的DNS就只有截短为512字节,那么用户得到的DNS应答就是不完整的。



为了克服这种困难,最简单的方式就是使用TCP,来重新查询。尽管交易时间可能比较长,但毕竟可以得到完整的答案,总比得到不完整的答案要好。



难道UDP没有办法传输超过大于576字节的数据吗?



并不是这样。



DNS是由于自身的限制,原因上述文字已经解释。



当基于UDP传输的DNS有1000字节需要传输时,会将1000字节砍成两个500字节的报文传输?



不会!只会保留前面的512字节,剩下的488字节会抛弃!



为什么要这样?



DNS没有字段来标识报文ID,比如1、2、3,所以默认只有一个报文,剩下的多余数据只有被扔的份!



互联网的不安全性



互联网经过多年的发展,鱼龙混杂,欺骗横行,用户的域名服务器是8.8.8.8,用户能够拍着胸脯说,从8.8.8.8返回的DNS应答真的就是8.8.8.8回答的吗?



不能!



无论是采用UDP、还是TCP传输,都无法保证!



怎么办呢?



如果8.8.8.8 返回的应答,使用自己的私钥签名,那么主机得到应答之后,先检查签名是否来自于8.8.8.8的签名,如果是,接收。如果不是,拒绝!



这样是不是就可以拍着胸脯说,应答真的是来自于8.8.8.8。



但签名带来了很多负面问题,DNS应答由于携带了证书链,整个报文有几千字节,无法使用UDP传输,那也只好使用TCP传输了。



总结一下



使用UDP传输是由于效率高,传输小于等于512字节报文。



使用TCP传输是由于可以传输大于512字节报文。



使用签名是保证数据来源的可靠性。



使用TCP传输,同样是可以传输证书链、签名。



使用UDP同样可以传输远远大于576字节的数据,只要应用程序可以标识数据ID。



DNS在区域传输的时候使用TCP协议,其他时候使用UDP协议。



DNS区域传输的时候使用TCP协议:



1.辅域名服务器会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。区域传送使用TCP而不是UDP,因为数据同步传送的数据量比一个请求应答的数据量要多得多。



2.TCP是一种可靠连接,保证了数据的准确性。



域名解析时使用UDP协议:



客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过三次握手,这样DNS服务器负载更低,响应更快。理论上说,客户端也可以指定向DNS服务器查询时用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。



DNS为什么用TCP和UDP
DNS同时占用UDP和TCP端口53是公认的,这种单个应用协议同时使用两种传输协议的情况在TCP/IP栈也算是个另类。但很少有人知道DNS分别在什么情况下使用这两种协议。



先简单介绍下TCP与UDP。
TCP是一种面向连接的协议,提供可靠的数据传输,一般服务质量要求比较高的情况,使用这个协议。UDP—用户数据报协议,是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。



TCP与UDP的区别:
UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同。TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息,否则将一直等待直到收到确认信息为止。 与TCP不同,UDP协议并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据报的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP协议称为不可靠的传输协议。相对于TCP协议,UDP协议的另外一个不同之处在于如何接收突发性的多个数据报。不同于TCP,UDP并不能确保数据的发送和接收顺序。事实上,UDP协议的这种乱序性基本上很少出现,通常只会在网络非常拥挤的情况下才有可能发生。
既然UDP是一种不可靠的网络协议,那么还有什么使用价值或必要呢?其实不然,在有些情况下UDP协议可能会变得非常有用。因为UDP具有TCP所望尘莫及的速度优势。虽然TCP协议中植入了各种安全保障功能,但是在实际执行的过程中会占用大量的系统开销,无疑使速度受到严重的影响。反观UDP由于排除了信息可靠传递机制,将安全和排序等功能移交给上层应用来完成,极大降低了执行时间,使速度得到了保证。



DNS在进行区域传输的时候使用TCP协议,其它时候则使用UDP协议;
DNS的规范规定了2种类型的DNS服务器,一个叫主DNS服务器,一个叫辅助DNS服务器。在一个区中主DNS服务器从自己本机的数据文件中读取该区的DNS数据信息,而辅助DNS服务器则从区的主DNS服务器中读取该区的DNS数据信息。当一个辅助DNS服务器启动时,它需要与主DNS服务器通信,并加载数据信息,这就叫做区传送(zone transfer)。



为什么既使用TCP又使用UDP?
首先了解一下TCP与UDP传送字节的长度限制:
UDP报文的最大长度为512字节,而TCP则允许报文长度超过512字节。当DNS查询超过512字节时,协议的TC标志出现删除标志,这时则使用TCP发送。通常传统的UDP报文一般不会大于512字节。



区域传送时使用TCP,主要有一下两点考虑:
1.辅域名服务器会定时(一般时3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,则会执行一次区域传送,进行数据同步。区域传送将使用TCP而不是UDP,因为数据同步传送的数据量比一个请求和应答的数据量要多得多。
2.TCP是一种可靠的连接,保证了数据的准确性。



域名解析时使用UDP协议:
客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过TCP三次握手,这样DNS服务器负载更低,响应更快。虽然从理论上说,客户端也可以指定向DNS服务器查询的时候使用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。



UDP
UDP 与 TCP 的主要区别在于 UDP 不一定提供可靠的数据传输。事实上,该协议不能保证数据准确无误地到达目的地。UDP 在许多方面非常有效。当某个程序的目标是尽快地传输尽可能多的信息时(其中任意给定数据的重要性相对较低),可使用 UDP。ICQ 短消息使用 UDP 协议发送消息。
许多程序将使用单独的TCP连接和单独的UDP连接。重要的状态信息随可靠的TCP连接发送,而主数据流通过UDP发送。



TCP
TCP的目的是提供可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。TCP在数据包接收无序、丢失或在交付期间被破坏时,负责数据恢复。它通过为其发送的每个数据包提供一个序号来完成此恢复。记住,较低的网络层会将每个数据包视为一个独立的单元,因此,数据包可以沿完全不同的路径发送,即使它们都是同一消息的组成部分。这种路由与网络层处理分段和重新组装数据包的方式非常相似,只是级别更高而已。
为确保正确地接收数据,TCP要求在目标计算机成功收到数据时发回一个确认(即 ACK)。如果在某个时限内未收到相应的 ACK,将重新传送数据包。如果网络拥塞,这种重新传送将导致发送的数据包重复。但是,接收计算机可使用数据包的序号来确定它是否为重复数据包,并在必要时丢弃它。



TCP与UDP的选择



如果比较UDP包和TCP包的结构,很明显UDP包不具备TCP包复杂的可靠性与控制机制。与TCP协议相同,UDP的源端口数和目的端口数也都支持一台主机上的多个应用。一个16位的UDP包包含了一个字节长的头部和数据的长度,校验码域使其可以进行整体校验。(许多应用只支持UDP,如:多媒体数据流,不产生任何额外的数据,即使知道有破坏的包也不进行重发。)  
很明显,当数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,TCP协议是当然的选择。当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。在数据传输时间很短,以至于此前的连接过程成为整个流量主体的情况下,UDP也是一个好的选择,如:DNS交换。把SNMP建立在UDP上的部分原因是设计者认为当发生网络阻塞时,UDP较低的开销使其有更好的机会去传送管理数据。TCP丰富的功能有时会导致不可预料的性能低下,但是我们相信在不远的将来,TCP可靠的点对点连接将会用于绝大多数的网络应用。


TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来。三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。



UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!
UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境。比如,我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。例如,在默认状态下,一次“ping”操作发送4个数据包(如图2所示)。大家可以看到,发送的数据包数量是4包,收到的也是4包(因为对方主机收到后会发回一个确认收到的数据包)。这充分说明了UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。HTTP是用TCP协议传输的。



TCP协议与UDP协议的区别



TCP基于面向连接的协议,数据传输可靠,传输速度慢,适用于传输大量数据,可靠性要求高的场合。



UDP协议面向非连接协议,数据传输不可靠,传输速度快,适用于一次只传送少量数据、对可靠性要求不高的应用环境。



面向连接的TCP



“面向连接”就是在正式通信前必须要与对方建立起连接。比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。



TCP协议能为应用程序提供可靠的通信连接,使一台计算机发出的字节流无差错地发往网络上的其他计算机,对可靠性要求高的数据通信系统往往使用TCP协议传输数据。



面向非连接的UDP协议



“面向非连接”就是在正式通信前不必与对方先建立连接,不管对方状态就直接发送。这与现在风行的手机短信非常相似:你在发短信的时候,只需要输入对方手机号就OK了。



UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境



UDP协议是面向非连接的协议,没有建立连接的过程。正因为UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。QQ就使用UDP发消息,因此有时会出现收不到消息的情况。



TCP协议与UDP协议支持的应用协议



TCP支持的应用协议主要有:Telnet、FTP、SMTP等;



UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。



TCP和UDP都是位于OSI模型中的传输层中。



TCP优点:面向连接的,具有实时性,就象打电话一样,两者必须建立连接.
它保证你所传输的东西是准确到达的,并且收方要给你一个收到或没有\ 收到的回复,所以它具有安全性的特点..
UDP优点:面向无连接的,就象给某人寄信一样,对方不需要在邮局等着你的信到.
所以说,它没有保障性,不能确保你一定能收到信,不象TCP那样,,但是 它比TCP好的一点,就是速度快,因为他不需要双方交流是否收到,对发的东西有一个确认的过程.



    server端


main.go
package main



import (
“fmt”
“net”
)



func main() {
listen, err := net.ListenUDP(“udp”, &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 9090,
})



if err != nil {
fmt.Printf("listen failed, err:%v\n", err)
return
}

for {
var data [1024]byte
n, addr, err := listen.ReadFromUDP(data[:])
if err != nil {
fmt.Printf("read failed from addr: %v, err: %v\n", addr, err)
break
}

fmt.Printf("addr: %v data: %v count: %v\n", addr, string(data[:n]), n)

_, err = listen.WriteToUDP([]byte("received success!"), addr)
if err != nil {
fmt.Printf("write failed, err: %v\n", err)
continue
}
} }   


client端



main.go
package main



import (
“fmt”
“net”
)



func main() {
//connect server
conn, err := net.DialUDP(“udp”, nil, &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 9090,
})



if err != nil {
fmt.Printf("connect failed, err: %v\n", err)
return
}

//send data
_, err = conn.Write([]byte("hello server!"))
if err != nil {
fmt.Printf("send data failed, err : %v\n", err)
return
}

//receive data from server
result := make([]byte, 4096)
n, remoteAddr, err := conn.ReadFromUDP(result)
if err != nil {
fmt.Printf("receive data failed, err: %v\n", err)
return
}
fmt.Printf("receive from addr: %v data: %v\n", remoteAddr, string(result[:n])) }



  1. 协程介绍
    协程又称微线程,从名字可以看出,协程的粒度比线程更小,并且是用户管理和控制的,多个协程可以运行在一个线程上面。那么协程出现的背景又是什么呢,先来看一下目前线程中影响性能的特性:



使用锁机制
线程间的上下文切换
线程运行和阻塞状态的切换
以上任意一点都是很消耗cpu性能的。相对来说协程是由程序自身控制,没有线程切换的开销,且不需要锁机制,因为在同一个线程中运行,不存在同时写变量冲突,在协程中操作共享资源不加锁,只需要判断状态就行了,所以执行效率比线程高的多。




  1. 主流语言对协程的支持
    Lua语言



Lua从5.0版本开始使用协程,通过扩展库coroutine来实现。



Python语言



python可以通过 yield/send 的方式实现协程。在python 3.5以后,async/await 成为了更好的替代方案。



Go语言



Go语言对协程的实现非常强大而简洁,可以轻松创建成百上千个协程并发执行。



Java语言



如上文所说,Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能,有兴趣的小伙伴可以看一看Kilim框架的源码:



https://github.com/kilim/kilim



C/C++



c/c++需要自己借助ucontext、setjmp、longjmp库实现,微信开源了c/c++的协程库libco。



1、协程(Coroutine):比线程的单位更小,在一个线程中可以开启多个协程,利用多个协程实现并发。



2、协程跟具有操作系统概念的线程不一样,实际上协程就是像函数一样的程序组件,可以在一个线程里面轻松创建数十万个协程,就像数十万次函数调用一样。只不过函数只有一个调用入口起始点,返回之后就结束了,而协程入口既可以是起始点,又可以从上一个返回点继续执行,也就是说协程之间可以通过 yield 方式转移执行权



3、举个栗子:



如果用多线程实现生产者-消费者模式,线程之间需要使用同步机制来避免产生全局资源的竟态,这就不可避免产生了休眠、调度、切换上下文一类的系统开销,而且线程调度还会产生时序上的不确定性。



而对于协程来说,“挂起”的概念只不过是转让代码执行权并调用另外的协程,待到转让的协程告一段落后重新得到调用并从挂起点“唤醒”,这种协程间的调用是逻辑上可控的,时序上确定的,可谓一切尽在掌握中。



协程与多线程对比:



优势:



1、协程极高的执行效率。单线程内就可以实现并发的效果,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。



2、不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。



劣势:



1、无法利用多核CPU



2、一旦协程出现阻塞,将会阻塞整个线程



解决方案:



多进程+协程:既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。



Category web