计算机网络:自顶向下方法读书笔记(四)
2.3 因特网中的电子邮件
该系统的主要组成部分包括客户端程序(useragent)、服务器端程序(mail server)以及简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)。
2.3.1 SMTP[RFC 5321]
2.3.2 与HTTP对比
-
区别之一
-
HTTP属于拉取协议(pull protocol),它通过从服务器处获取信息。
-
SMTP属于推送协议(push protocol),它负责将数据发送至服务器。
-
区别二
- 编码不同,SMTP要求每个报文采用7比特ASCII码
-
区别三
- 处理既包含文本又包含图形的文档的方式不同
-
2.3.3 邮件报文格式[RFC 5322]
2.3.4 邮件访问协议
第3版邮局协议(Post Office Protocol-Version 3,POP3)[RFC 1939]
- 第一阶段**(Authorization phase)**: 用户代理通过明文方式发送用户名和密码以完成身份验证
- 第二阶段**(Transaction phase)**: 接收回执并可执行以下操作: 对报文设置删除标记、取消删除标记以及获取邮件统计信息等
- 第三阶段**(Update phase)**通常在客户端代理执行quit命令后出现
需要注意的是POP3将邮件下载至本地供浏览使用,但人们希望在远程服务器上直接操作邮件
基于Web的电子邮件(HTTP)
HTTP
SMTP
HTTP
用户1
邮件服务器1
邮件服务器2
用户2
2.4 DNS:因特网的目录服务
主机标识
主机名 hostname 如www.baidu.com
IP地址 IP address 如121.7.106.83
2.4.1 DNS提供的服务
-
域名系统(Domain Name System, DNS) [RFC 1034\RFC 1035] 是一种基于分层架构设计的分布式数据库。
-
这些服务器通常采用 Berkeley Internet Name Domain (BIND) 软件作为操作系统。
-
应用层协议通过使主机能够对分布式数据库执行查询操作
-
DNS协议基于UDP协议运行,并采用53号端口
-
其他重要服务 * 主机别名(host aliasing)
-
规范主机名(canonical hostname)
-
邮件服务器别名(mail server aliasing)
-
负载分配(load distribution)
-
-
2.4.2 DNS工作机理概述
分布式、层次数据库 *
DNS服务器分为3类:
* 根DNS服务器
* 顶级域(Top-Level Domain,TLD)DNS服务器
* 权威DNS服务器

本地DNS服务器(Local DNS servers, 代理功能, 将请求转发至其所属层次结构中的服务器):每个ISP都配置了一台或多个本地DNS服务器(也被称为默认名字服务器)。当主机接入某个ISP时, 该ISP会返回主机对应的IP地址, 而这些IP地址通常会与相应的本地DNS服务器绑定。
各种DNS服务器的交互

DNS缓存(DNS caching):该机制表示,在某个DNS服务器接收到一个DNS查询响应时(即当它返回指向相应主机IP地址或其他资源的信息),会将相关信息存储在本地内存中。通常情况下,在两日后会清除这些缓存信息以释放系统资源。
2.4.3 DNS记录和报文
资源记录(Resource Record) * 广泛地存储于所有DNS服务器中
- 实现了主机名与IP地址之间的对应关系
- (Name, Value, Type, TTL)
- TTL指定了记录的有效存续时间
- Name与Value根据具体的Type而定
- 当Type=A时, Name即为主机名称, 其对应的IP地址即为此主机的网络接口地址
- 当Type=NS时, Name即为域名称, 其对应的网络接口地址由权威DNS服务器提供
- 当Type=CNAME时, 其规范hostname由该别名指向
- 当Type=MX时, 其规范hostname由该邮件服务器提供
DNS报文
- DNS只有查询报文和回答报文两种报文,且格式相同 *

2.5 P2P文件发放
这一节主要注意的是
P2P和客户-服务器体系结构的分发时间的对比 :

其中

BitTorrent的简易原理
- 文件块(chunk): 洪流中的各个对等方在下载文件时会同步获取相同大小的文件块(通常以256千字节为单位)。
- 洪流(torrent): 所有参与分发该文件的对等方组成的集合。
- 追踪器(tracker): 当某个对等方加入特定洪流时,必须向追踪器登记自身信息以便实时监控和记录该洪流内所有对等方的状态变化。
- 最稀缺优先(rarest first): 该算法会优先处理那些本机不具备且在其邻近节点中也同样稀缺的文件块(即副本数量最少的情况),从而实现各份副本尽可能均匀分布在洪流中。
- 疏通(unchoked): 每隔10秒确认连接到网络中传输速度最快的4个邻居,则被定义为‘畅通无阻’连接。
2.6 视频流和内容分发网络
2.7 套接字编程(Python)
2.7.1 UDP套接字编程
目标地址:目的地的IP地址 + 套接字的端口号
套接字实验小程序内容:
- 客户端接收用户的输入信息,并将其以单行文本的形式发送给服务端。
- 服务端接收到数据后进行处理,并将其内容转换为全大写形式。
- 经过处理的数据被传输至客户端设备。
- 客户端设备接收到传输的数据后进行解码,并将其展示在用户界面中。
# Python 网络通信库
from socket import *
# 服务器的主机名orIP地址(方便实验用的本机地址) 和 服务器的端口号
serverName = '127.0.0.1'
serverPort = 12000
# 创建客户端套接字
# 第一个参数指的是地址簇 AF_INET 特别指示了底层网络使用IPv4
# 第二个参数指的是套接字类型 SOCK_DGRAM 表示的是UDP套接字类型
# 注意这里客户套接字没有指定端口号,因为操作系统会生成
clientSocket = socket(AF_INET, SOCK_DGRAM)
# input是py内置方法,参数为提示内容,键盘输入将存入message
message = input('Input lowercase sentence:')
# encode方法用于将字符串类型转为字节类型,附上目标地址,(源地址自动附上)
clientSocket.sendto(message.encode(), (serverName, serverPort))
# 等待返回的数据并存入modifiedMessage中,源地址放入serverAddress中,缓存长度为2048
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
# 从字节转换为字符串后输出
print(modifiedMessage.decode())
# 关闭套接字进程
clientSocket.close()
# Python 网络通信库
from socket import *
# 服务器的端口号
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
# 将端口号和套接字绑定
serverSocket.bind(('', serverPort))
print('The server is ready to receive')
while True:
message, clientAddress = serverSocket.recvfrom(2048)
modifiedMessage = message.decode().upper()
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
2.7.2 TCP套接字编程
与UDP的不同
- 基于连接的协议通常需要发起通信握手并创建TCP连接。
- 建立之后,只需将数据发送到该端口,并不需携带目的地址。
需要区分欢迎套接字 和连接套接字 :
* 前者是用来初次接触的
* 后者是接触后生成的专用通道
from socket import *
serverName = '127.0.0.1'
serverPort = 12000
# 第二个参数指的是套接字类型 SOCK_STREAM 表示的是TCP套接字类型
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, serverPort))
sentence = input('Input lowercase sentence:')
clientSocket.send(sentence.encode())
modifiedSentence = clientSocket.recv(1024)
# 从字节转换为字符串后输出
print('From Server:', modifiedSentence.decode())
# 关闭套接字进程
clientSocket.close()
from socket import *
serverPort = 12000
# serverSocket为欢迎套接字
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
# 规定了请求连接的最大数目(最少为1)
serverSocket.listen(1)
print('The server is ready to receive')
while True:
# connectionSocket为连接套接字
connectionSocket, addr = serverSocket.accept()
sentence = connectionSocket.recv(1024).decode()
capitalizedSentence = sentence.upper()
connectionSocket.send(capitalizedSentence.encode())
connectionSocket.close()
