应用层
# 应用层协议原理
互联网流行的应用层协议:
- HTTP
- FTP
- SMTP/POP3/IMAP
- DNS
网络应用的例子:
- Web
- 文本消息
- 远程登录
- P2P共享文件
- 即时通讯
- 多用户网络游戏
- 流媒体
- Internet电话
- 实时电视会议
- 社交网络
- 搜索
- ......
网络应用的体系架构
- 客户-服务器模式(C/S:client/server)
- 对等模式(P2P:peer to peer)
- 混合体:客户-服务器和对等体系结构
# 套接字
套接字用于解决分布式进程的通信(即不同主机间进程的通信)。同一主机内的进程,使用进程间通信机制通信(操作系统定义)。
# 对进程进行编址
一个进程:用IP+PORT
标示端节点。本质上,一对主机进程之间的通信由2个端节点构成。
一些知名端口号的例子:
- HTTP:TCP 80
- Mail:TCP 25
- FTP:TCP 21
# 传输层提供的服务
层间接口必须要携带的信息:源IP、源TCP(UDP)端口号、目标IP和目标TCP(UDP)端口号
# TCP Socket
对于使用面向连接服务(TCP)的应用而言,套接字是4元组的一个具有本地意义的标示
- 4元组:(源IP,源port,目标IP,目标port)
- 唯一的指定了一个会话(2个进程之间的会话关系)
- 应用使用这个标示,与远程的应用进程通信
- 不必在每一个报文的发送都要指定这4元组
- 就像使用操作系统打开一个文件,OS返回一个文件句柄一样,以后使用这个文件句柄,而不是使用这个文件的目录名、文件名
- 简单,便于管理
# UDP Socket
对于使用无连接服务(UDP)的应用而言,套接字是2元组的一个具有本地意义的标示
- 2元组:IP,port (源端指定)
- UDP套接字指定了应用所在的一个端节点(end point)
- 在发送数据报时,采用创建好的本地套接字(标示ID),就不必在发送每个报文中指明自己所采用的ip和port
- 但是在发送报文时,必须要指定对方的ip和udp port(另外一个段节点)
# 传输层给应用层提供的服务
应用层需要传输层提供的服务?如何描述传输层的服务?
- 数据丢失率
- 吞吐
- 延迟
- 安全
# internet传输层提供的服务
# TCP服务
- 可靠的服务
- 流量控制:发送方不会淹没接收方
- 拥塞控制:当网络拥塞时,能抑制发送方
- 面向连接:要求客户端进程与服务端进程之间建立连接
- 不能提供的服务:时间保证、最小吞吐量和安全
# UDP服务
- 不可靠的数据传输
- 不提供的服务:可靠,流量控制、拥塞控制、时间、宽带保证、建立连接
UDP存在的必要性
能够区分不同的进程,而IP服务不能
- 在IP提供的主机到主机端到端功能的基础上,区分了主机的应用进程
无需建立连接,省去了建立连接时间,适合事务性的应用
不做可靠性的工作,例如检错重发,适合那些对实时性要求比较高而对正确性要求不高的应用
- 因为为了实现可靠性(准确性、保序等),必须付出时间代价(检错重发)
没有拥塞控制和流量控制,应用能够按照设定的速度发送数据
- 而在TCP上面的应用,应用发送数据的速度和主机向网络发送的实际速度是不一致的,因为有流量控制和拥塞控制
# 安全TCP
TCP & UDP
- 都没有加密
- 明文通过互联网传输,甚至密码
SSL
- 在TCP上面实现,提供加密的TCP连接
- 私密性
- 数据完整性
- 端到端的鉴别
SSL在应用层
- 应用采用SSL库,SSL库使用TCP通信
SSL socket API
- 应用通过API将明文交给socket,SSL将其加密在互联网上传输
# HTTP(超文本传输协议)
- 使用TCP
- HTTP是无状态的
# HTTP连接
非持久HTTP
- 最多只有一个对象在TCP连接上发送
- 下载多个对象需要多个TCP连接
- HTTP/1.0使用非持久连接
持久HTTP
- 多个对象可以在一个(客户端和服务器之间)TCP连接上传输
- HTTP/1.1默认使用持久连接
# HTTP方法类型
HTTP/1.0
- GET
- POST
- HEAD
- 要求服务器在响应报文中不包含请求对象->故障跟踪
HTTP/1.1
- GET
- POST
- HEAD
- PUT
- 将实体主体中的文件上载到URL字段规定的路径
- DELETE
- 删除URL字段规定的文件
# HTTP响应状态码
200 OK
- 请求成功,请求对象包含在响应报文的后续部分
301 Moved Permanently
- 请求的对象已经被永久转移了;新的URL在响应报文的Location:首部行中指定
- 客户端软件自动用新的URL去获取对象
400 Bad Request
- 一个通用的差错代码,表示该请求不能被服务器解读
404 Not Found
- 请求的文档在该服务上没有找到
505 HTTP Version Not Supported
# DNS(Domain Name Server)
DNS负责域名与IP地址的转换。
# DNS的总体思路和目标
DNS的主要思路
分层的、基于域的命名机制
若干分布式的数据库完成名字到IP地址的转换
运行在UDP之上端口号为53的应用服务
核心的Internet功能,但以应用层协议实现
- 在网络边缘处理复杂性
DNS主要目的
- 实现主机名-IP地址的转换(name/IP translate)
- 其它目的
- 主机别名到规范名字的转换:Host aliasing
- 邮件服务器别名到邮件服务器的正规名字的转换:Mail server aliasing
- 负载均衡:Load Distribution
# DNS记录
DNS :保存资源记录(RR)的分布式数据库
RR 格式:(name, value, type, ttl)
type说明:
type=A(A记录)
- name为主机名【即域名】
- value为IP地址
type=CNAME
name为规范名字的别名
www.ibm.com 的规范名字为 servereast.backup2.ibm.com
value为规范名字【即真正的域名】
type=NS
- name为域名(如foo.com)【子域名字】
- value为该域名的权威服务器的域名【子域名字服务器的名字】
type=MX
- value为name对应的邮件服务器的名字
TTL:生存时间,决定了资源记录应当从缓存中删除的时间
# DNS大致工作过程
- 应用调用解析器(resolver)
- 解析器作为客户,向name server发出查询报文(封装在UDP段中)
- name server返回响应报文(name/ip)
# 名字服务器及其查询方式
# 名字服务器
- 本地名字服务器(Local Name Server)
- 无严格的分层结构
- 名字服务器(Name Server)
- 名字服务器的解析过程
- 目标名字存在Local Name Server中
- 情况1:查询的名字在该区域中
- 情况2:缓存(cashing)
- 目标名字存在Local Name Server中
- 名字服务器的解析过程
当与本地名字服务器不能解析名字时,联系根名字服务器顺着根-TLD 一直找到 权威名字服务器
# 查询方式
- 递归查询
- 迭代查询
# Socket编程
# UDP Socket
==UDP在客户端和服务器之间没有连接==
- 没有握手
- 发送端在每一个报文中明确地指定目标的IP地址和端口号
- 服务器必须从收到的分组中提取出发送端的IP地址和端口号
==UDP传送的数据可能乱序,也可能丢失==
进程视角看UDP服务:UDP 为客户端和服务器提供不可靠的字节组的传送服务
udpClient.py
from socket import * server_name = 'localhost' server_port = 12000 # 创建UDP Socket client_socket = socket(AF_INET, SOCK_DGRAM) # 输入需要传输的信息 message = input(">") # 将服务器名称、端口附加到消息;发送到socket client_socket.sendto(message.encode(), (server_name, server_port)) # 将套接字中的应答字符读到字符串 modified_message, server_address = client_socket.recvfrom(2048) # 打印结果 print(modified_message.decode()) client_socket.close
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17udpServer.py
from socket import * server_port = 12000 # 创建UDP Socket server_socket = socket(AF_INET, SOCK_DGRAM) # 绑定本地端口号12000 server_socket.bind(("", server_port)) print("The server is ready to receive") while True: # 从socket中接收消息和客户端地址(IP和端口号) message, client_address = server_socket.recvfrom(2048) modified_message = message.decode().upper() server_socket.sendto(modified_message.encode(), client_address)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# TCP Socket
==服务器首先运行,等待连接建立==
1、服务器进程必须先处于运行状态
- 创建欢迎socket
- 和本地端口捆绑
- 在欢迎socket上阻塞式等待接收用户的连接
==客户端主动和服务器建立连接:==
2、创建客户端本地套接字(隐式捆绑到本地port)
- 指定服务器进程的IP地址和端口号,与服务器进程连接
3 、当与客户端连接请求到来时
- 服务器接受来自用户端的请求,解除阻塞式等待,返回一个新的socket(与欢迎socket不一样),与客户端通信
- 允许服务器与多个客户端通信
- 使用源IP和源端口来区分不同的客户端
4、连接API调用有效时,客户端P与服务器建立了TCP连接
从应用程序的角度:TCP在客户端和服务器进程之间提供了可靠的、字节流(管道)服务
tcpClient.py
from socket import * server_name = "localhost" server_port = 12000 # 为远程服务器创建TCP Socket client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect((server_name, server_port)) sentence = input(">") # No need to attach server name, port client_socket.send(sentence.encode()) modified_sentence = client_socket.recv(1048) print('From Server:', modified_sentence.decode()) client_socket.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15tcpServer.py
from socket import * server_port = 12000 # 创建TCP Welcome Socket server_socket = socket(AF_INET, SOCK_STREAM) server_socket.bind(('', server_port)) # server begins listening for incoming TCP requests server_socket.listen(1) print('The server is ready to receive') while True: # server waits on accept() for incoming requests, new socket created on return connection_socket, addr = server_socket.accept() # read bytes from socket (but not address as in UDP) sentence = connection_socket.recv(1024).decode() capitalized_sentence = sentence.upper() # close connection to this client (but not welcoming socket) connection_socket.send(capitalized_sentence.encode()) connection_socket.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25