计算机网络应用层:DNS、P2P和Socket编程
  jcgWI5cZ3bG5 2023年11月01日 55 0

DNS

域名系统Domain Name System,DNS)的主要任务是主机名到IP地址的转换的目录服务。DNS是:

  • 一个由分层DNS服务器实现的分布式数据库;
  • 一个使得主机能够查询分布式数据库的应用层协议;

DNS服务器是运行BINDBerkeley Internet Name Domain)软件的UNIX机器,运行在UDP之上,使用53号端口。

DNS被其他应用层协议所使用,举例某用户主机请求某URL:

  1. 该主机上运行着DNS应用的客户端;
  2. 浏览器从URL中抽取主机名,并将主机名传给DNS客户端;
  3. DNS客户端向DNS服务器发送包含主机名的请求;
  4. DNS服务器返回主机名对应的IP,主机浏览器得到IP后,向其80端口的HTTP服务器进程发起TCP连接;

除提供IP转换以外,DNS还具有以下服务

  • 主机别名host aliasing):有着复杂主机名的主机能拥有一个或多个别名。举例:relay1.west-coast.enterprise.com主机的别名可能是enterprise.comwww.enterprise.com。此时,relay1.west-coast.enterprise.com称为规范主机名canonical hostname),主机别名更加容易记忆。
  • 邮件服务器别名mail server aliasing):类似主机别名,一般规范主机名比较复杂,别名容易记忆,调用DNS可以得到主机名的规范主机名和IP地址;
  • 负载分配load distribution):繁忙的站点被冗余分布在多台服务器上,每台有不同的IP,所以一个IP地址集合与同一个规范主机名对应,用户请求时,DNS用整个IP地址集合响应,但每个回答中循环使用这些IP地址,在服务器间循环分配了负载;

DNS工作机理综述:

分布式、层次数据库

DNS使用了大量的DNS服务器,以层次方式组织。大致说来有3类DNS服务器:根DNS服务器顶级域Top-Level DomainDNS服务器权威DNS服务器。假设一个DNS客户要决定www.amazon.com的IP地址,首先与根服务器之一联系,得到顶级域名com的TLD服务器IP,再与这些TLD服务器之一联系,将为amazon.com返回权威DNS服务器IP,最后,客户与amazon.com权威服务器之一联系,返回www.amazon.com的IP地址。

  • 根DNS服务器:有400多个根DNS服务器遍布,由13个不同的组织管理。根DNS服务器提供TLD服务器的IP地址;
  • 顶级域(TLD)DNS服务器:每个顶级域(com、org、net、edu和gov)以及国家的顶级域(uk、fr、ca和jp),都有TLD服务器。TLD服务器提供权威DNS服务器的IP地址;
  • 权威DNS服务器:每个在 Internet 上拥有可公开访问的主机(例如 Web 服务器和邮件服务器)的组织都必须提供可公开访问的 DNS 记录,将这些主机的名称映射到 IP 地址。 组织的权威 DNS 服务器包含这些 DNS 记录。 组织可以选择实施自己的权威 DNS 服务器来保存这些记录;或者,组织可以付费将这些记录存储在某些服务提供商的权威 DNS 服务器中。 大多数大学和大公司都实施和维护自己的一级和二级(备份)权威 DNS 服务器。

根、TLD和权威DNS服务器都处在该DNS服务器的层次结构中。

还有另一类重要的DNS服务器,称为本地DNS服务器local DNS server)。严格说来,一个本地DNS服务器并不属于该服务器的层次结构,但它对DNS层次结构是至关重要的。

每个ISP(如一个居民区的1SP或一个机构的ISP)都有一台本地DNS服务器(也叫默认名字服务器)。当主机与某个ISP连接时,该ISP提供一台主机的IP地址,该主机具有一台或多台其本地DNS服务器的IP地址(通常通过DHCP)。通过访问Windows或UNIX的网络状态窗口,用户能够容易地确定他的本地DNS服务器的IP地址。 主机的本地DNS服务器通常“邻近”本主机。对某机构ISP而言,本地DNS服务器可能就与主机在同一个局域网中;对于某居民区ISP来说,本地DNS服务器通常与主机相隔不超过几台路由器。当主机发岀DNS请求时,该请求被发往本地DNS服务器,它起着代理的作用,并将该请求转发到DNS服务器层次结构中。

举例:

假设主机cse.nyu.edu想知道主机gaia.cs.umass.edu的IP地址。同时假设(NYU)的cse.nyu.edu主机的本地DNS服务器为dns.nyu.edu,并且gaia.cs.umass.edu的权威DNS服务器为dns.umass.edu
如图所示:

  1. 主机cse.nyu.edu首先向它的本地DNS服务器dns.nyu.edu发送一个DNS查询报文。该查询报文含有被转换的主机名gaia.cs.umass.edu;
  2. 本地DNS服务器将该报文转发到根DNS服务器;
  3. 该根DNS服务器注意到其edu前缀并向本地DNS服务器返回负责edu的TLD服务器的IP地址列表;
  4. 该本地DNS服务器则再次向这些TLD服务器之一发送查询报文;
  5. 该TLD服务器注意到umass.edu前缀,并用权威DNS服务器的IP地址进行响应,该权威DNS服务器是负责umass的dns.umass.edu;
  6. 最后,本地DNS服务器直接向dns.umass.edu重发查询报文;
  7. dns.umass.edu用gaia.cs.umass.edu的IP地址进行响应;

注意到在本例中,为了获得一台主机名的映射,共发送了8份DNS报文:4份查询报文和4份回答报文!利用DNS缓存能减少这种査询流量

图中所示的例子利用了递归查询(recursive query)和迭代查询(iterative query)。

  • 从cse.nyu.edu到dns.nyu.edu发出的查询是递归查询,因为该查询以自己的名义请求dns.nyu.edu来获得该映射。
  • 而后继的3个查询是迭代查询,因为所有的回答都是直接返回给dns.nyu.edu。

从理论上讲,任何DNS查询既可以是迭代的也能是递归的。


 

DNS缓存:

DNS缓存DNS caching),实际上,为了改善时延性能并减少在因特网上到处传输的DNS报文数量,DNS广泛使用了缓存技术。
DNS缓存的原理非常简单。在一个请求链中,当某DNS服务器接收一个DNS回答(例如,包含某主机名到IP地址的映射)时,它能将映射缓存在本地存储器中。
例如,在上图中,每当本地DNS服务器dns.nyu.edu从某个DNS服务器接收到一个回答,它能够缓存包含在该回答中的任何信息。如果在DNS服务器中缓存了一台主机名/IP地址对,另一个对相同主机名的查询到达该DNS服务器时,该DNS服务器就能够提供所要求的IP地址,即使它不是该主机名的权威服务器。DNS服务器在一段时间后(通常设置为两天)将丢弃缓存的信息。

DNS记录和报文:
共同实现DNS分布式数据库的所有DNS服务器存储了资源记录(Resource Record, RR), RR提供了主机名到IP地址的映射。每个DNS回答报文包含了一条或多条资源记录。
资源记录是一个包含了下列字段的4元组:

(Name, Valuer,Type, TTL)

TTL是该记录的生存时间,它决定了资源记录应当从缓存中删除的时间。在下面给岀的记录例子中,我们忽略掉TTL字段。Name和Value的值取决于Type:

  • 如果Type = A,则Name是主机名,Value是该主机名对应的IP地址。因此,一条类型为A的资源记录提供了标准的主机名到IP地址的映射。例如(relay1.bar.foo.com, 145.37.93.126, A)就是一条类型A记录。
  • 如果Type = NS,则Name是个域(如foo.com),而Value是个知道如何获得该域中主机IP地址的权威DNS服务器的主机名。这个记录用于沿着查询链来路由DNS查询。例如(foo.com, dns.foo.com, NS)就是一条类型为NS的记录。
  • 如果Type = CNAME,则Value是别名为Name的主机对应的规范主机名。该记录能够向査询的主机提供一个主机名对应的规范主机名,例如(foo.com, relay1.bar.foo.com, CNAME)就是一条CNAME类型的记录。
  • 如果Type = MX,则Value是个别名为Name的邮件服务器的规范主机名。举例来说,(foo.com, mail.bar.foo.com, MX)就是一条MX记录。MX记录允许邮件服务器主机名具有简单的别名。值得注意的是,通过使用MX记录,一个公司的邮件服务器和其他服务器(如它的Web服务器)可以使用相同的别名。为了获得邮件服务器的规范主机名,DNS客户应当请求一条MX记录;而为了获得其他服务器的规范主机名,DNS客户应当请求CNAME记录。

DNS报文:

DNS只有查询和回答报文,且有着相同的格式:

  • 前12个字节是首部区域,其中有几个字段。第一个字段(标识符)是一个16比特的数,用于标识该查询。这个标识符会被复制到对查询的回答报文中,以便让客户用它来匹配发送的请求和接收到的回答。 标志字段中含有若干标志。1比特的“查询/回答”标志位指出报文是查询报文(0)还是回答报文(1)。当某DNS服务器是所请求名字的权威DNS服务器时,1比特的“权威的”标志位被置在回答报文中。如果客户(主机或者DNS服务器)在该DNS服务器没有某记录时希望它执行递归查询,将设置1比特的“希望递归”标志位。如果该DNS服务器支持递归查询,在它的回答报文中会对1比特的“递归可用”标志位置位。在该首部中,还有4个有关数量的字段,这些字段指出了在首部后的4类数据区域出现的数量。
  • 问题区域包含着正在进行的查询信息。该区域包括:①名字字段,包含正在被查询的主机名字;②类型字段,指出有关该名字的正被询问的问题类型,例如主机地址是与一个名字相关联(类型A)还是与某个名字的邮件服务器相关联(类型MX)。
  • 在来自DNS服务器的回答中,回答区域包含了对最初请求的名字的资源记录。前面讲过每个资源记录中有Type(如A、NS、CNAME和MX)字段、Value字段和TTL字段。在回答报文的回答区域中可以包含多条RR,因此一个主机名能够有多个IP地址(例如,就像本节前面讨论的冗余Web服务器)。
  • 权威区域包含了其他权威服务器的记录。
  • 附加区域包含了其他有帮助的记录。例如,对于一个MX请求的回答报文的回答区域包含了一条资源记录,该记录提供了邮件服务器的规范主机名。该附加区域包含一个类型A记录,该记录提供了用于该邮件服务器的规范主机名的IP地址。

DNS数据库中插入记录:

假定你刚刚创建一个称为网络乌托邦(Network Utopia)的新创业公司。你必定要做的第一件事是在注册登记机构注册域名networkutopia.com注册登记机构registrar)是一个商业实体,它验证该域名的唯一性,将该域名输入DNS数据库。
当你向某些注册登记机构注册域名networkutopia.com时,需要向该机构提供你的基本和辅助权威DNS服务器的名字和IP地址。假定该名字和IP地址是dnsl.networkutopia.comdns2.networkutopia.com212.212.212.1212.212.212.2
对这两个权威DNS服务器的每一个,该注册登记机构确保将一个类型NS和一个类型A的记录输入TLD com服务器。特别是对于用于networkutopia.com的基本权威服务器,该注册登记机构将下列两条资源记录插入该DNS系统中:

(networkutopia.com, dns1.networkutopia.com, NS)
(dns1.networkutopia.com, 212.212.212.1, A)

你还必须确保用于Web服务器www.networkutopia.com的类型A资源记录和用于邮件服务器的mail.networkutopia.com的类型MX资源记录被输入你的权威DNS服务器中。


 

P2P文件分发

P2P文件分发中,每个对等方能够向任何其他对等方重新分发它已经收到的该文件的任何部分,从而在分发过程中协助该服务器。到2016年止,最为流行的P2P文件分发协议是BitTorrent
P2P体系结构到的扩展性:

 

 

 

 

 

BitTorrent:

BitTorrent是一种用于文件分发的流行P2P协议。参与一个特定文件分发的所有对等方的集合被称为一个洪流torrent),在一个洪流中的对等方彼此下载等长度的文件块chunk),典型的块长度为256KB。当一个对等方首次加入一个洪流时,它没有块。随着时间的流逝,它累积了越来越多的块。当它下载块时,也为其他对等方上载了多个块。一旦某对等方获得了整个文件,它也许(自私地)离开洪流,或(大公无私地)留在该洪流中并继续向其他对等方上载块。同时,任何对等方可能在任何时候仅具有块的子集就离开该洪流,并在以后重新加入该洪流中。
每个洪流具有一个基础设施节点,称为追踪器tracker)。当一个对等方加入某洪流时,它向追踪器注册自己,并周期性地通知追踪器它仍在该洪流中。以这种方式,追踪器跟踪参与在洪流中的对等方。一个给定的洪流可能在任何时刻具有数以百计或数以千计的对等方。

  • 如何决定请求哪个块?最稀缺优先rarest first)的技术。这种技术的思路是,针对一个客户没有的块在她的邻居中决定最稀缺的块(最稀缺的块就是那些在她的邻居中副本数量最少的块),并首先请求那些最稀缺的块。这样,最稀缺块得到更为迅速的重新分发,其目标是(大致地)均衡每个块在洪流中的副本数量。
  • 为了决定响应哪个请求,BitTorrent使用了一种机灵的对换算法。其基本想法是:
  1. 用户A根据当前能够以最高速率向她提供数据的邻居,给出其优先权。
  2. 特别是,A对于她的每个邻居都持续地测量接收到比特的速率,并确定以最高速率流入的4个邻居。
  3. 每过10秒,她重新计算该速率并可能修改这4个对等方的集合。
  4. 用BitTorrent术语来说,这4个对等方被称为疏通(unchoked)。
  5. 重要的是,每过30秒,她也要随机地选择另外一个邻居并向其发送块。我们将这个被随机选择的对等方称为B。
  6. 因为A正在向B发送数据,她可能成为B前4位上载者之一,这样的话B将开始向A发送数据。
  7. 如果B向A发送数据的速率足够高,B接下来也能成为A的前4位上载者。
  8. 换言之,每过30秒A将随机地选择一名新的对换伴侣并开始与那位伴侣进行对换。如果这两名对等方都满足此对换,它们将对方放入其前4位列表中并继续与对方进行对换,直到该对等方之一发现了一个更好的伴侣为止。这种效果是对等方能够以趋向于找到彼此的协调的速率上载。随机选择邻居也允许新的对等方得到块,因此它们能够具有对换的东西。除了这5个对等方(“前”4个对等方和一个试探的对等方)的所有其他相邻对等方均被“阻塞”,即它们不能从A接收到任何块。BitTorrent有一些有趣的机制没有在这里讨论,包括片(小块)、流水线、随机优先选择、残局模型和反怠慢。

 

套接字编程:

  • 客户从其键盘读取一行字符(数据)并将该数据向服务器发送;
  • 服务器接收该数据并将这些字符转换为大写;
  • 服务器将修改的数据发送给客户;
  • 客户接收修改的数据并在其监视器上将该行显示出来;

简单的UDP套接字编程


UDPClient.py

from socket import *
server_name = "localhost"
server_port = 12000

client_socket = socket(AF_INET, SOCK_DGRAM)
while True:
    message = input("input lowercase: ")
    if message == "quit":
        break
    client_socket.sendto(message.encode(), (server_name, server_port))

    modified_message, server_address = client_socket.recvfrom(2048)
    print(modified_message.decode())
client_socket.close()

UDPServer.py

from socket import *

server_port = 12000

server_socket = socket(AF_INET, SOCK_DGRAM)
server_socket.bind(("", server_port))
print("server is ready to receive")
while True:
    message, client_address = server_socket.recvfrom(2048)
    print(f"receive {message}")
    modified_message = message.decode().upper()
    server_socket.sendto(modified_message.encode(), client_address)


简单的TCP套接字编程
与UDP不同,TCP是一个面向连接的协议。这意味着在客户和服务器能够开始互相发送数据之前,它们先要握手和创建一个TCP连接。TCP连接的一端与客户套接字相联系,另一端与服务器套接字相联系。当创建该TCP连接时,我们将其与客户套接字地址(IP地址和端口号)和服务器套接字地址(IP地址和端口号)关联起来。使用创建的TCP连接,当一侧要向另一侧发送数据时,它只需经过其套接字将数据丢进TCP连接。这与UDP不同,UDP服务器在将分组丢进套接字之前必须为其附上一个目的地地址。

服务器程序必须具有一扇特殊的门,更精确地说是一个特殊的套接字,该门欢迎来自运行在任意主机上的客户进程的某种初始接触。使用房子与门来比喻进程与套接字,有时我们将客户的初始接触称为“敲欢迎之门” 。

随着服务器进程的运行,客户进程能够向服务器发起一个TCP连接。这是由客户程序通过创建一个TCP套接字完成的。当该客户生成其TCP套接字时,它指定了服务器中的欢迎套接字的地址,即服务器主机的IP地址及其套接字的端口号。生成其套接字后,该客

户发起了一个三次握手并创建与服务器的一个TCP连接。发生在运输层的三次握手,对于客户和服务器程序是完全透明的。
在三次握手期间,客户进程敲服务器进程的欢迎之门。当该服务器“听”到敲门声时,它将生成一扇新门(更精确地讲是一个新套接字),它专门用于特定的客户。

 

TCPClient.py

from socket import *

server_name = "localhost"
server_port = 12000

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect((server_name, server_port))
while True:
    message = input("input lowercase: ")
    if message == "quit":
        break
    client_socket.send(message.encode())
    modified_message = client_socket.recv(1024)
    print(f"modified: {modified_message.decode()}")

client_socket.close()


TCPServer.py

from socket import *

server_port = 12000

server_socket = socket(AF_INET, SOCK_STREAM)
server_socket.bind(("", server_port))
server_socket.listen(2)
print("server is ready receive")
while True:
    connect_socket, client_address = server_socket.accept()
    message = connect_socket.recv(1024).decode()
    print(f"receive {message}")
    modified_message = message.upper()
    connect_socket.send(modified_message.encode())
    connect_socket.close()

 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  5NWiQFAVeqgX   11天前   10   0   0 网络安全
  OKgNPeBk991j   10天前   27   0   0 网络安全
  rKgO6TN7xbYO   11天前   20   0   0 网络安全