文章目录
应用层
应用层协议原理
应用层网络应用体系结构有三种,老生常谈的东西
- 客户机/服务器
- 对等 (P2P)
- 客户机/服务器与P2P的混合
应用层的通信,本质是两台不同主机间进程的通信,如果两个进程在同一台主机上,进程间的通信由操作系统控制;如果是不同主机的两个进程,通过网络报文进行
套接字是应用程序和网络之间的应用程序接口API ,是在网络上建立网络应用程序的可编程接口,是进程通信的接口
用户代理:是用户与网络应用程序之间的接口,如浏览器Web应用的用户代理
套接字识别进程的话分为两步,识别网络中的哪一个主机,它用主机的地址标识,还需要识别主机中的哪一个进程, 它用端口号标识
因特网运输层给应用层提供的服务有
- 传输控制协议TCP (面向连接)
过程是:建立连接(握手过程),传输报文,拆除连接
通信进程可以无差错、按适当顺序交付发送的数据,当发送方和接收方之间的网络出现拥塞时,会抑制发送进程速率 - 用户数据报协议UDP(面向无连接)
过程:两个进程通信前没有握手过程
不保证报文能够被接收,或收到的报文是乱序到达,发送进程可以任何速率发送数据
TCP和UPD都没有提供任何加密机制,加密层由应用程序实现,它位于应用层和传输层之间,由安全层套接字层实现(Secure Socket Layer, SSL)
下面是常见应用对应的应用层协议和传输协议
2022/3/11更新
Web和HTTP
一,基本概念
HTTP超文本传输协议,是web的核心,它定义了报文的格式以及客户机和服务器交换报文的方式
一个web页面,就是一个文档,一个文档由若干对象组成,一个对象就是一个文件,对象比如说:HTML文件,JPEG图片等等
每个对象都能在网络中定位,通过url统一资源标识符
![image-20220311161558454](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220311161558454.png)
客户机和服务器的交互过程,包含 创建TCP连接→交换报文→关闭TCP连接,底层是通过TCP建立的可靠连接,但是HTTP是无状态的,客户机请求多次,服务器也会响应多次
二,HTTP连接
首先是非持续连接,基本上现在的网络中不会用,每个TCP连接只传输一个请求报文和一个响应报文,服务器返回对象后关闭,每次请求一个对象都要重新连接
TCP连接的“三次握手”过程,如下
- 客户机发送一个TCP连接请求报文
- 服务器回送一个TCP确认响应报文
- 客户机向服务器发送一个包含“ HTTP请求”与“TCP确认”的报文
![image-20220311162734626](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220311162734626.png)
非持续连接缺点:服务器负担重,每个对象传输时间延长(每个对象都要两个RTT时延)
持续连接
相同客户机与服务器之间的后续请求和响应报文通过相同的连接进行传送,省去了再次建立连接的过程,不过一定时间间隔未被使用,服务器会关闭连接
持续连接包含,非流水线方式和流水线方式
- 非流水线方式:客户机只能在前一个响应接收到之后才能发出新的请求
- 流水线方式:客户机可一个接一个连续产生请求(只要有引用就产生),即在前一个请求接收到响应之前可以产生新的请求
![image-20220311163204451](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220311163204451.png)
http请求的报文如下
三,用户与服务器交互:Cookie
HTTP服务器是无状态的,不保存客户信息,所以需要需要Cookie让服务器与用户相关联
- 在HTTP响应报文中有一个cookie 首部行
- 在HTTP请求报文中有一个cookie 首部行
- 用户主机中保留有一个 cookie 文件并由浏览器管理
- Web站点的后端数据库保存cookie
工作流程:
![image-20220311163837427](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220311163837427.png)
四,Web缓存器
Web缓存器(Web cache):也叫代理服务器,它存在的意义在于加快客户机想服务器请求对象的速度,原理如下:
浏览器向缓存发送所有HTTP请求,如果在Web缓存中,就之间返回该对象,如果不在,则代理服务器替用户想原始服务器发送请求,接收后转发给客户机,和高速缓存类似,减少响应时间,减少内部和因特网的通信量
五,条件GET方法
在使用Web缓存的时候,存放在缓存中的对象拷贝可能是旧的。即保存在起始Web服务器中的对象可能已经被修改,条件GET方法可以使得缓存的对象一直是最新状态,流程如下
- 客户机第一次请求时候,Web服务器回发响应报文给Web缓存器,包括对象的最后修改时间Last-modified:date1,Web缓存器记录时间并且缓存对象
- 客户机再次请求,缓存器检查Web服务器中的该对象是否已被修改,发送一个条件GET请求报文If-modified-since: date1
- 若Web服务器中的该对象未被修改,则响应报文含有304 Not Modified,并且实体为空;如果修改过,再次重新发送响应报文,携带Last-modified:date2
2022/3/16更新
文件传输协议FTP
本地主机上的用户,向远程主机上传或者下载文件,用户通过一个FTP用户代理与FTP服务器交互
![image-20220316150426410](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220316150426410.png)
FTP运行在TCP之上,FTP建立了两个并行的连接控制连接和数据连接
- 控制连接
在21号端口上建立,用于在两主机间传输控制信息(如用户标识、口令等),它是持续连接的:在整个用户会话期间一直保持
- 数据连接
在20端口建立,用于准确传输文件,在该数据连接上传送一个文件并关闭连接,它是非持续连接的:会话中每进行一次文件传输,都需要建立一个新的数据连接
FTP较HTTP来说,是带外传输,是分离控制的;还有是FTP协议是有状态,会对用户行为进行追踪,并控制会话总数
电子邮件SMTP(讨论应用层)
电子邮件系统的总体结构由三部分组成:用户代理,邮件服务器,简单邮件传输协议SMTP
![image-20220316151518662](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220316151518662.png)
用户代理
即邮件阅读器,允许用户阅读、回复、发送、保存和撰写报文,用户接收和发送邮件都用通过用户代理
比如说:qq邮箱,阿里邮箱,Foxmail
![image-20220316151850958](https://liangyuanshao.oss-cn-shenzhen.aliyuncs.com/img/image-20220316151850958.png)
邮件服务器(mail server)
邮件服务器里面由报文队列,存储要转发的邮件报文;自身并且存储了用户的所有邮件
用户访问自己邮箱时,邮件服务器对其身份进行验证(用户名和口令),若投递失败,发送方将其保存在一个报文队列中,以后每30分钟发送一次,若几天后仍未成功,将该报文删除,并通知发送方
简单邮件传送协议SMTP
SMTP使用端口为25,并且SMTP不使用中间邮件服务器发送邮件
把一封邮件从发送邮件服务器传送到接收邮件服务器的过程: 如Alice 向 Bob发送报文
SMTP较HTTP来说共同点和不同点
相同点:
- 都用于从一台主机向另一台主机传送文件
- 都是持续连接
不同点:
- SMTP是推协议:发送邮件服务器把文件推向接收邮件服务器,其TCP连接是由要发送文件的机器发起。
- HTTP是拉协议:用户使用HTTP从服务器拉取信息,其TCP连接是由想获取文件的机器发起。
- SMTP对传输的数据有限制,只能传输7位ASCII码,对一些包含了非7位ASCII字符的报文或二进制数据(如图片、声音),需要按照7位ASCII码进行编码,再传送
- HTTP数据没有该限制
- SMTP所有报文对象放在一个报文中
- HTTP把每个对象封装在它各自的HTTP响应报文中发送
MIME(多用途因特网邮件扩展):用于非ASCII数据传输。将非ASCII数据编码后传输,接收方再解码还原,增加新的MIME邮件首部,接收的报文还有Received字段首部行
邮件访问协议
邮件访问协议就是取邮件的协议,因为SMTP协议只能发送邮件,取邮件有其它的协议,有POP3(第三版的邮局协议),IMAP(因特网邮件访问协议),HTTP
- POP3
在用户代理打开了一个到邮件服务器(服务器)端口110上的TCP连接后,身份认证后,开始工作,功能有限,取回后自动删除(这是缺陷) - IMAP
在用户的PC机上运行IMAP客户程序,然后与ISP的邮件服务器上的IMAP服务器程序建立TCP连接,是联机协议(实现复杂) - HTTP
用户使用浏览器收发电子邮件,发件人使用HTTP 将电子邮件报文从其浏览器发送到其邮件服务器上,然后收件人使用HTTP从其邮箱中取一个报文到浏览器。容易进行目录管理。
DNS(因特网的目录服务)
标识主机的方式有主机名和ip地址,主机名容易记忆,通常需要将主机名检索出对应的ip地址,DNS提供此服务,进行主机名到IP地址的转换
DNS运行在UDP协议之上,使用53端口,DNS通常直接由其他的应用层协议 (包括HTTP、SMTP 和FTP)使用,以将用户提供的主机名解析为IP地址。用户只是间接使用
DNS工作机理概述
- 某个应用程序调用DNS的客户端,并指明需要被转换的主机名
- 用户主机上的DNS接收到后,向网络中发送一个DNS查询报文
- 经过若干毫秒到若干秒的时延后,用户主机上的DNS接收到一个提供所希望映射的DNS回答报文
- 映射结果被传递到调用DNS的应用程序
DNS设计
- 简单设计(集中式设计)
假设因特网上只使用一个DNS服务器,该服务器包含所有的映射
优点:设计简单
缺点:单点故障,通信容量,远距离请求导致延时长,维护困难 - 分布式、分层次式设计
为了处理扩展性问题,DNS使用了大量的DNS服务器,它们以层次方式组织,并且分布在全世界范围内
- 第一层为根DNS服务器:世界上有13台,是冗余服务器,具有可靠性
- 第二层为顶级域(TLD)DNS服务器:负责顶级域名如net、gov,以及所有国家的顶级域名如uk、fr、ca和jp
- 第三层为权威DNS服务器:在因特网上具有公共可访问主机的组织机构用来保存将主机名映射为IP地址的DNS记录。多数大学和大公司实现和维护它们基本和辅助的权威DNS服务器
- 本地DNS服务器,不算DNS服务器层次结构,可以起缓存代理作用,减少访问次数,提高效率,由于主机和主机名与IP地址间的映射并不是永久的,DNS服务器在一段时间后(通常设置为两天)将丢弃缓存的信息
交互过程
迭代查询
递归查询
DNS记录报文
资源记录是一个包含了下列字段的4元组**(Name,Value,Type,TTL)**
-
TTL决定记录生存时间
-
Name和Value的值取决于Type
- Type=A,Name则是主机名,Value是该主机对应的IP地址。提供了标准的主机名到IP地址的映射。(relayl.bar.foo.com,145.37.93.126,A)
- Type=NS,Name是一个域(如foo.com),Value是个知道如何获得该域中主机IP地址的权威DNS服务器主机名。该记录用于沿着查询链来路由DNS查询。 (foo.com,dns.foo.com,NS)
- Type=CNAME,Value是别名为Name的主机对应的规范主机名。向查询的主机提供一个主机名对应的规范主机名。(foo.com,relayl.bar.foo.com,CNAME)
- Type=MX,Value是别名为Name的邮件服务器的规范主机名。(foo.com,mail.bar.foo.com,MX)MX记录允许邮件服务器主机名具有简单的别名。
如果服务器不是用于某主机名的权威服务器,则该服务器会有一条类型NS记录,该记录对应于包含主机名的域;还将包含一条类型A记录
DNS报文结构
这里面的请求流程比较难懂,希望大伙能够自己琢磨一会,我花了2个小时才看明白/(ㄒoㄒ)/~~
例子:当请求www.liangyuanshao.top的时候
- 我这台主机向本地DNS服务器发请求
- 本地DNS服务器请求根域名服务器,根域名服务器返回dns.top的顶级域名服务器地址
- 本地DNS服务器请求dns.top顶级域名服务器,dns.top顶级域名服务器返回dns.liangyuanshao.top权威域名服务器的地址
- 本地DNS服务器请求上面dns.liangyuanshao.top权威域名服务器的地址,它返回服务器主机的ip地址
- DNS根据服务器把ip地址告诉我这台主机,主机建立和服务器的TCP通信
2022/3/26更新
P2P应用
P2P文件分发:从单一服务器向大量主机分发一个大文件,每个对等方能够重新分发它所有的该文件的任何部分,从而在分发过程中协助该服务器(P2P文件共享协议BitTorrent)
特性:
- 直接在对等方间传输:所有内容不经过第三方服务器
- 高度的可扩展能力:利用众多对等方集合中的资源去分发内容
- 使用客户机/服务器模式:请求的对等方是客户机,被选中的对等方是服务器
P2P自扩展性
分发时间:所有N个对等方得到该文件的副本所需要的时间
自扩展的直接成因:对等方除了是比特的消费者外还是它们的重新分发者
P2P文件分发协议BitTorrent
洪流(torrent):参与一个特定文件分发的所有对等方的集合
追踪器:每个洪流所具有的一个基础设施结点
BitTorrent如何进行文件分发?采用最稀缺优先的技术
- 针对她没有的块在她的邻居中决定最稀缺的块(邻居中副本数量最少的块),并首先请求那些最稀缺的块
- 使最稀缺块得到更为迅速的重新分发——(大致地)均衡每个块在洪流中的副本数量。
分布式散列表(DHT)
DHT是一个由广域范围大量结点共同维护的巨大散列表。散列表被分割成不连续的块,每个结点被分配给一个属于自己的散列块,并成为这个散列块的管理者。
环形DHT:每个对等方并不需要保存整个系统所有对等方的信息,每个对等方仅与它的直接后继和直接前任联系
优点: 每个对等方只需要知道两个对等方,即它的直接后继和直接前任。
缺点:为了找到负责的键(在最差的情况下),DHT中的所有个结点将必须绕环转发该报文,平均发送N/2条报文
依据缺点,我们可用增加捷径:DHT能被设计成每个对等方的邻居数量以及每个请求的报文数量均为O(log N),其中N是对等方的数量
除此之外,我们还要关注对等方扰动:在P2P系统中,对等方能够不加警示地到来和离去。因此,当设计一个DHT时,我们也必须关注存在这种对等方扰动时维护DHT的情况
因此:每个对等方要能够联系其第一个和第二个后继
**当某对等方突然离开时DHT如何维护DHT?**找到离开对等方标记的前一个对等方,然后重新指定它的下一个对等方和下下个对等方,重新维护
**当某对等方突然加入时DHT如何维护DHT?**找到对等方标记的前一个对等方即可,然后重新维护
内容定位体系结构
一个对等方如何确定哪个对等方有所需要的内容?
集中式目录
目录服务器(大型服务器或服务器场):提供目录服务,收集可共享的对象,建立集中式的动态数据库(对象名称到IP地址的映射)。
缺点:单点故障,性能瓶颈,侵犯版权,可靠性(文件传输是分散的,但定位内容的过程是高度集中的)
查询洪泛:Gnutella
全分布方式:无中心服务器,通过洪泛查询,找到所需对象,就是广发询问
缺点:扩展性差, “查询报文”在网络中产生很大的流量
覆盖网络
- 向覆盖网络中的每个邻居发送“查询报文”;
- 每个邻居再向邻居转发,使覆盖网络上的每个对等方都能收到该查询
- 如果收到查询的对等方中有被请求对象,沿反向路径回发“查询命中”报文
层次覆盖网络,KaZaA: 查询
每组包括若干个组员,一个组长
组员与其组长有一个TCP连接,将共享内容告诉组长
组长维护一个数据库,该组的共享内容及相关对等方的IP地址。
相关组长之间建立TCP连接
组长追踪其所有子节点上的内容
请求排队:限制并行上载数量
激励优先权:上载文件比下载文件多的用户优先
并行下载:从多个对等方请求并下载同一个文件的不同部分
TCP编程
客户机程序和服务器程序运行时,分别创建一个客户机进程和一个服务器进程,相互之间通过套接字读写数据进行通信
TCP套接字编程
运行在不同机器上的进程彼此通过套接字传递报文来进行通信
客户机和服务器程序之间的交互
- 建立TCP连接
- 传送数据
java程序示例:
客户端:
import java.io.*;
import java.net.*;
class TCPClient {
public static void main(String argv[ ]) throws Exception
{
String sentence;
String modifiedSentence;
BufferedReader inFromUser =
new BufferedReader(new InputStreamReader(System.in));
Socket ClientSocket = new Socket("hostname", 6789);
DataOutputStream outToServer =
new DataOutputStream(ClientSocket.getOutputStream());
BufferedReader inFromServer =
new BufferedReader(new
InputStreamReader(ClientSocket.getInputStream()));
sentence = inFromUser.readLine();
outToServer.writeBytes(sentence + '\n');
modifiedSentence = inFromServer.readLine();
System.out.println("FROM Server: " + modifiedSentence);
ClientSocket.close();
}
}
服务器端:
import java.io.*;
import java.net.*;
class TCP Server {
public static void main(String argv[]) throws Exception
{
String ClientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);
while(true) {
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient =
new BufferedReader(new
InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient =
new DataOutputStream(connectionSocket.getOutputStream());
ClientSentence = inFromClient.readLine();
capitalizedSentence = ClientSentence.toUpperCase() + '\n';
outToClient.writeBytes(capitalizedSentence);
}
}
}
UDP套接字编程
- 通信进程之间没有初始握手,不需要欢迎套接字
- 没有流与套接字相联系
- 发送主机将信息字节封装生成分组,再发送
- 接收进程解封收到的分组,获得信息字节
java程序示例:
客户端:
import java.io.*;
import java.net.*;
class UDPSocket {
public static void main(String args[]) throws Exception
{
BufferedReader inFromUser =
new BufferedReader(new InputStreamReader(System.in));
DatagramSocket ClientSocket = new DatagramSocket();
InetAddress IPAddress = InetAddress.getByName("hostname");
byte[ ] sendData = new byte[1024];
byte[ ] receiveData = new byte[1024];
String sentence = inFromUser.readLine();
sendData = sentence.getBytes();
DatagramPacket sendPacket =
new DatagramPacket(sendData, sendData.length, IPAddress, 9876);
ClientSocket.send(sendPacket);
DatagramPacket receivePacket =
new DatagramPacket(receiveData, receiveData.length);
ClientSocket.receive(receivePacket);
String modifiedSentence =
new String(receivePacket.getData());
System.out.println("FROM Server:" + modifiedSentence);
ServerSocket.close();
}
}
服务器端:
import java.io.*;
import java.net.*;
class UDPServer {
public static void main(String args[]) throws Exception
{
DatagramSocket ServerSocket = new DatagramSocket(9876);
byte[] receiveData = new byte[1024];
byte[] sendData = new byte[1024];
while(true)
{
DatagramPacket receivePacket =
new DatagramPacket(receiveData, receiveData.length);
ServerSocket.receive(receivePacket);
String sentence = new String(receivePacket.getData());
InetAddress IPAddress = receivePacket.getAddress();
int port = receivePacket.getPort();
String capitalizedSentence = sentence.toUpperCase();
sendData = capitalizedSentence.getBytes();
DatagramPacket sendPacket =
new DatagramPacket(sendData, sendData.length, IPAddress,
port);
ServerSocket.send(sendPacket);
}
}
}
这一章我们学到的知识整体的思路框架: