前言
计算机网络核心知识系列博客分为上、中、下部分,此篇为(上)。
博客的内容是计算机网络的核心知识,但也仅仅限于求得“管中窥一豹之形体”,也就是说这是入门级别的博客,若想深入的话,还是得好好啃大部头。
文章目录
- 前言
- 1 计算机网络概述(上)
- 1.1 计算机网络基本概念
- 1.1.1 什么是计算机网络?
- 1.1.2 通信系统模型
- 1.1.3 定义:计算机网络就是`互连`的、`自治`的计算机集合。
- 1.1.4 距离远、数量大如何保证互连?
- 1.1.5 什么是Internet?-组成细节角度
- 1.1.6 什么是Internet?-服务角度
- 1.1.7问题
- 1.2 计算机网络结构
- 1.2.1 什么是网络协议?
- 1.2.2 协议的三要素
- 1.3 网络核心
- 1.3.1 数据交换-电路交换
- 多路复用
- 1.3.2 数据交换-报文、分组交换(1)
- 报文交换
- 分组交换
- 1.3.3 数据交换-报文、分组交换(2)
- 1.3.4 数据交换-报文、分组交换(3)
- 1 计算机网络概述(下)
- 1.4 计算机网络性能
- 1.4.1 速率
- 1.4.2 带宽
- 1.4.3 延迟/时延(delay或latency)
- 1.4.4 四种分组延迟
- 1.4.5 时延带宽积(单位:bits(比特))
- 1.4.6 分组丢失(丢包)
- 1.4.7 吞吐量/率(Throughput)
- 1.5 计算机网络体系结构
- 1.5.1 计算机网络体系结构概述
- 复杂系统的分层结构
- 为何采用分层结构?
- 分层网络体系结构基本概念
- 1.5.2 OSI参考模型(1)
- OSI参考模型解释的通信过程
- OSI参考模型数据封装与通信过程
- 为什么需要数据封装?
- 1.5.3 OSI参考模型(2)
- 物理层功能
- 数据链路层功能
- 网络层功能
- 1.5.4 OSI参考模型(3)
- 传输层功能
- 会话层功能
- 表示层功能
- 应用层功能
- 1.5.5 TCP/IP参考模型
- 1.5.6 "5层"参考模型
- 5层模型的数据封装
- 1.6 计算机网络发展历史
- 1.6.1 "1961-1972": 早期分组交换原理的提出与应用
- 1.6.2 "1972-1980": 网络互连,大量新型、私有网络的涌现
- 1.6.3 "1980-1990": 新型网络协议与网络的激增
- 1.6.4 "1990, 2000's": 商业化 , Web, 新应用
- 1.6.5 "2005~今"
- 2 应用层
- 2.1 网络应用(层)内容概述
- 2.2 网络应用的基本原理
- 2.2.1 网络应用的体系结构
- 客户机/服务器结构(Client-Server, C/S)
- 点对点结构(Peer-to-peer, P2P)
- 混合结构(Hybrid)
- 2.2.2 网络应用进程通信
- 网络应用的基础:进程间通信
- 套接字: Socket
- 如何寻址进程
- 应用层协议
- 应用层协议的内容
- 2.2.3 网络应用需求
- 2.2.4 Internet提供的传输(层)服务
- 2.3 Web应用
- 2.3.1 Web应用概述
- Web与HTTP
- HTTP协议概述
- 2.3.2 HTTP连接类型
- 非持久性连接(Nonpersistent HTTP)
- 持久性连接(Persistent HTTP)
- 2.3.3 HTTP消息格式
- HTTP请求消息
- HTTP请求消息的通用格式
- 上传输入的方法
- 方法的类型
- HTTP响应消息
- HTTP响应状态代码
- 体验一下HTTP
- 2.3.4 Cookie技术
- 为什么需要Cookie?
- Cookie技术
- Cookie的原理
- Cookie的作用
- 2.3.5 Web缓存技术/代理服务器技术
- 条件性GET方法
- 2.4 Email应用
- 2.4.1 Email应用概述
- Email应用的构成
- SMTP协议: RFC 2821
- Email应用示例
- SMTP交互示例
- 动手尝试SMTP交互
- SMTP协议
- 2.4.2 Email消息格式和POP协议
- Email消息格式
- Email消息格式:多媒体扩展
- 邮件访问协议
- 2.5 DNS(域名解析系统)
- 2.5.1 DNS概述
- DNS:Domain Name System
- DNS
- 分布式层次式数据库
- DNS记录缓存和更新
- 2.5.2 DNS记录和消息格式
- DNS记录
- DNS协议和消息格式
- 如何注册域名?
- 2.6 P2P应用
- 2.6.1 P2P应用:原理与文件分发
- 文件分发:客户机/服务器vs. P2P ,到底谁快?
- 2.6.2 文件分发:BitTorrent
- BitTorrent 基本原理(1)
- BitTorrent 基本原理(2)
- 2.6.3 P2P应用:索引技术
- P2P: 搜索信息
- 集中式索引技术
- 集中式索引的问题
- 2.6.4 洪泛式查询索引技术:Query flooding
- 2.6.5 层次式覆盖网络索引技术
- 案例:Skype
- 2.7 Socket编程
- 2.7.1 Socket编程-应用编程接口(API)
- 网络程序设计接口
- 应用编程接口 API
- 几种典型的应用编程接口
- 2.7.2 Socket编程-Socket API概述
- Socket API
- 如何在服务器端找到想找的接口呢?
- Socket抽象
- Socket的结构
- 2.7.3 Socket编程-Socket API函数(1)
- Socket编程架构(以WinSock为例)
- WSAStartup
- WSACleanup
- socket
- Closesocket
- bind
- 2.7.4 Socket编程-Socket API函数(2)
- listen
- connect
- accept
- send, sendto
- recv, recvfrom
- setsockopt, getsockopt
- 2.7.5 Socket API函数小结
- 2.7.6 关于网络字节顺序
- 2.7.7 网络应用的Socket API调用基本流程
- 2.7.8 Socket编程-客户端软件设计
- 解析服务器IP地址
- 解析服务器(熟知)端口号
- 解析协议号
- TCP客户端软件流程
- UDP客户端软件流程
- 客户端软件的实现- `connectsock()`
- 客户端软件的实现-UDP客户端
- 客户端软件的实现-TCP客户端
- 客户端软件的实现-异常处理
- 例1:访问DAYTIME服务的客户端(TCP)
- 例2:访问DAYTIME服务的客户端(UDP)
- 2.7.9 Socket编程-服务器软件设计
- 4种类型基本服务器
- 循环无连接服务器基本流程
- 数据发送
- 获取客户端点地址
- 循环面向连接服务器基本流程
- 并发无连接服务器基本流程
- 并发面向连接服务器基本流程
- 服务器的实现
- 服务器的实现-`passivesock() `
- 服务器的实现-`passiveUDP() `
- 服务器的实现-`passiveTCP() `
- 例1:无连接循环DAYTIME服务器
- 例2:面向连接并发DAYTIME服务器
- 3 传输层(上)
- 3.1 传输层服务
- 3.1.1 本章学习内容概括
- 3.1.2 传输层服务概述
- 3.1.3 传输层 vs. 网络层
- 3.1.4 Internet传输层协议
- 3.2 复用和分用
- 3.2.1 分用如何工作?
- 3.2.2 无连接分用
- 3.2.3 面向连接的分用
- 3.2.4 面向连接的分用:多线程Web服务器
- 3.3 无连接传输协议-UDP
- 3.3.1 UDP校验和(checksum)
- 校验和计算示例
- 3.4 可靠数据传输的基本原理
- 3.4.1 可靠数据传输概述
- 可靠数据传输协议基本结构:接口
- 可靠数据传输协议
- 3.4.2 Rdt 1.0:可靠信道上的可靠数据传输
- 3.4.3 Rdt 2.0:产生位错误的信道
- FSM规约
- 无错误场景
- 有错误场景
- 3.4.4 Rdt 2.1和2.2
- Rdt 2.0有什么缺陷?
- Rdt 2.1: 发送方, 应对ACK/NAK破坏
- Rdt 2.1: 接收方, 应对ACK/NAK破坏
- Rdt 2.1 vs. Rdt 2.0
- 3.4.5 Rdt 2.2: 无NAK消息协议
- FSM片段
- 3.4.6 Rdt 3.0
- Rdt 3.0发送方FS
- Rdt 3.0示例(1)
- Rdt 3.0示例(2)
- Rdt 3.0性能分析
- Rdt 3.0: 停等操作
- 3.4.7 流水线机制与滑动窗口协议
- 流水线协议
- 滑动窗口协议
- 3.4.8 Go-Back-N(GBN)协议
- Go-Back-N(GBN)协议: 发送方
- GBN: 发送方扩展FSM
- Go-Back-N(GBN)协议: 接收方
- GBN: 接收方扩展FSM
- GBN示例
- 练习题
- 3.4.9 Selective Repeat(SR)协议
- Selective Repeat:发送方/接收方窗口
- SR协议运作方式示例
- SR协议的“困境”
- 3.4.10 可靠数据传输原理与协议回顾
- 4 传输层(下)
- 4.1 面向连接传输协议-TCP
- 4.1.1 TCP概述
- 4.1.2 TCP可靠数据传输
- TCP可靠数据传输概述
- TCP:RTT和超时
- TCP发送方事件
- TCP发送端程序的伪代码
- TCP重传示例
- TCP-各种需重传场景下ACK该如何生成
- 快速重传机制
- 快速重传算法
- 4.1.3 TCP流量控制
- TCP流量控制原理详述
- 4.1.4 TCP连接管理
- 4.2 拥塞控制原理
- 4.2.1 拥塞控制原理(1)
- 拥塞的成因和代价:场景1
- 拥塞的成因和代价:场景2
- 拥塞的成因和代价:场景3
- 4.2.2 拥塞控制原理(2)
- 拥塞控制的方法
- 案例:ATM ABR拥塞控制
- 4.3 TCP拥塞控制及TCP性能分析
- 4.3.1 TCP拥塞控制
- 加性增—乘性减: `AIMD`
- TCP慢启动: `SS`
- Threshold变量
- Loss事件的处理
- TCP拥塞控制: 总结
- 4.3.2 TCP性能分析
- TCP throughput: 吞吐率
- 未来的TCP
- TCP的公平性
- 4.4 传输层总结
内容提纲:
1 计算机网络概述(上)
1.1 计算机网络基本概念
1.1.1 什么是计算机网络?
计算机网络=通信技术+计算机技术
计算机网络是通信技术
与计算机技术
紧密结合的产物。
1.1.2 通信系统模型
- 计算机网络就是一种通信网络
1.1.3 定义:计算机网络就是互连
的、自治
的计算机集合。
自治
:无主从关系互连
:互联互通- 通信链路
1.1.4 距离远、数量大如何保证互连?
通过交换网络
互连主机
1.1.5 什么是Internet?-组成细节角度
- 全球最大的
互联网络
- ISP(Internet Service Provider)网络互连的“
网络之网络
”
- 数以百万计的互连的
计算设备
集合主机
(hosts)=端系统
(end systems)- 运行各种网络应用
通信链路
- 光纤、铜缆、无线电、卫星…
分组交换
:转发分组(数据包)路由器
(routers)和交换机
(switches)
- 数以百万计的互连的
1.1.6 什么是Internet?-服务角度
为网络应用提供通信服务的通信基础设施:
Web、VolP、Email、网络游戏、电子商务、社交网络…为网络应用提供应用编程借口(API):
- 支持应用程序"连接"Internet,发送/接受数据
- 提供类似于邮政系统的数据传输服务
1.1.7问题
仅有硬件(主机、链路、路由器...)连接,Internet能否顺畅运行?能保证应用数据有序交付吗?...
回答是不能。还需要协议。
1.2 计算机网络结构
1.2.1 什么是网络协议?
网络协议(network protocol)
,简称协议
,是为进行网络中的数据交换而建立的规则、标准或约定。协议
规定了通信实体之间所交换的消息的格式
、意义
、顺序
以及针对收到信息或发生的事件所采取的"动作(actions)
"。
协议是计算机网络有序运行的重要保证。常见的协议有TCP,IP,HTTP,Skype,802.11等。
网络协议为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
例如,网络中一个微机用户和一个大型主机的操作员进行通信,由于这两个数据终端所用字符集不同,因此操作员所输入的命令彼此不认识。为了能进行通信,规定每个终端都要将各自字符集中的字符先变换为标准字符集的字符后,才进入网络传送,到达目的终端之后,再变换为该终端字符集的字符。当然,对于不相容终端,除了需变换字符集字符外还需转换其他特性,如显示格式、行长、行数、屏幕滚动方式等也需作相应的变换。
1.2.2 协议的三要素
语法
(Syntax)- 数据与控制信息的结构或格式
- 信号电平
语义
(Semantics)- 需要发出何种控制信息
- 完成何种动作以及做出何种相应
- 差错控制
时序
(Timing)- 事件顺序
- 速度匹配
协议规范了网络中所有信息发送和接收过程,是学习网络的重要内容之一。
Internet协议标准
- RFC:Request for Comments
- IETF:互联网工程任务组(Internet Engineering Task Force)。于此可获得关于协议的最新、最权威信息。
- 硬件(主机、路由器、通信链路等)是计算机网络的基础
- 计算机网络中的数据交换必须遵守事先约定好的
规则
- 如同交通系统
- 任何通信或信息交换过程都需要规则
1.3 网络核心
互联的路由器网络。
1.3.1 数据交换-电路交换
数据交换
:实现数据通过网络核心从源主机到达目的主机。
- 为什么需要数据交换?
-
N
2
N^2
N2链路问题
如果计算机网络中有 N N N台计算机,则每一台计算机需要有 N − 1 N-1 N−1条链路,这是不现实的。 - 保持计算机网络的
连通性
- 适用于不同的
网络规模
-
N
2
N^2
N2链路问题
- 什么是交换?
- 动态转接
- 动态分配传输资源
- 动态转接
- 数据交换的类型
电路交换
-
最典型电路交换网络:电话网络
-
电路交换的三个阶段:
- 建立连接(呼叫/电路建立)
- 通信
- 释放连接(拆除电路)
-
独占资源
-
电路交换网络如何共享中继线?
多路复用(Multiplexing)
-
报文交换
分组交换
多路复用
多路复用(multiplexing)
,简称复用
,是通信技术中的基本概念。
- 什么是多路复用?
链路/网络资源(如带宽)划分为“资源片”- 将资源片分配给各路“呼叫”(calls)
- 每路呼叫
独占
分配到的资源片进行通信 - 资源片可能“
闲置
”(idle
) (无共享,因为是分配好的)
典型多路复用方法
:频分
多路复用( frequency division multiplexing-FDM )时分
多路复用( time division multiplexing-TDM )波分
多路复用(Wavelength division multiplexing-WDM)码分
多路复用( Code division multiplexing-CDM )
- 频分多路复用FDM
将信号分为频率不同的几段。
- 频分多路复用的各用户占用不同的带宽资源(请注意,这里的“带宽 ”是
频率带宽
(单位:Hz)而不是数据的发送速率) - 用户在分配到一定的频带后,在通信过程中
自始至终都占用这个频带
- 频分多路复用的各用户占用不同的带宽资源(请注意,这里的“带宽 ”是
- 时分多路复用TDM
- 时分复用则是将时间划分为一段段等长的
时分复用帧
(TDM 帧),每个用户在每个TDM帧中占用固定序号的时隙 - 每用户所占用的时隙是
周期性出现
(其周期就是TDM帧的长度)
- 时分复用的所有用户是在不同的时间占用
相同的
频带宽度
- 时分复用则是将时间划分为一段段等长的
- 波分多路复用WDM
- 波分复用就是光的频分复用 (准确来说是光的波长复用)
- 波分复用就是光的频分复用 (准确来说是光的波长复用)
- 码分多路复用CDM
- 广泛应用于无线链路共享 (如蜂窝网,卫星通信等)
- 每个用户分配一个唯一的 m bit
码片序列
(chipping sequence),其中“0”用“-1
”表示、“1”用“+1
”表 示,
例如: S 站的码片序列:(–1 –1 –1 +1 +1 –1 +1 +1) - 各用户使用
相同频率
载波,利用各自码片序列编码数据 编码信号
=(原始数据) × × ×(码片序列)- 如发送比特 1(+1),则发送自己的
m bit 码片序列
- 如发送比特 0(-1),则发送该码片序列的
m bit 码片序列的反码
- 如发送比特 1(+1),则发送自己的
- 各用户码片序列相互
正交
(orthogonal)
1 m S i ⋅ S j = { 1 , i = j 0 , i ≠ j 1 m S i ⋅ S j ‾ = { − 1 , i = j 0 , i ≠ j \frac{1}{m}S_i\cdot S_j =\begin {cases} 1,& i=j \\ 0,&i\not=j \end{cases} \quad \frac{1}{m}S_i\cdot \overline{S_j} =\begin {cases} -1,& i=j \\ 0,&i\not=j \end{cases} m1Si⋅Sj={1,0,i=ji=jm1Si⋅Sj={−1,0,i=ji=j - 令
{
d
i
}
\{di\}
{di}为原始数据序列,各用户的叠加向量为
解码
: 码片序列与编码信号的内积
1 m S i ⋅ P = { 1 , S i ∈ P − 1 , S i ‾ ∈ P 0 , S i , S i ‾ ∉ P \frac{1}{m}S_i\cdot P=\begin{cases} 1, & S_i\in P \\ -1,& \overline{S_i}\in P \\ 0, & S_i,\overline{S_i}\notin P \end{cases} m1Si⋅P=⎩⎪⎨⎪⎧1,−1,0,Si∈PSi∈PSi,Si∈/P- 举例:单用户
- 举例:多用户
1.3.2 数据交换-报文、分组交换(1)
报文交换
报文
:源(应用)发送信息整体 (比如:一整
个文件 )
分组交换
现代网络通信数据交换基本使用分组交换
技术。
分组
:报文分拆出来的一系列相对较小的数据包
- 分组交换需要报文的
拆分
与重组
- 产生
额外开销
- 分组交换:统计多路复用(Statistical Multiplexing)
不同端分发的文件的分组同时进入链路。
如图带宽为1.5Mb/s。
带宽是不变的,极端情况例如A分发了文件,而B没有分发文件,则链路中A分发文件的分组占了1.5Mb/s带宽;
若A与B都分发了文件,则他们分发文件的分组一共占1.5Mb/s带宽。
由此可知统计多路复用技术不会有资源(链路带宽)闲置
的情况出现。 - 存储-转发(store-and-forward)交换方式
报文交换
与分组交换
均采用存储-转发
交换方式- 区别:
- 报文交换以
完整
报文进行“存储-转发” - 分组交换以较小的
分组
进行“存储-转发” - 问题:报文交换与分组交换孰优孰劣?下一节将回答这个问题。
- 报文交换以
1.3.3 数据交换-报文、分组交换(2)
衡量哪种交换方式更好的标准之一是分组交换的传输延迟的大小
。
- 传输延迟
发送主机:- 接收应用报文(消息)
- 拆分为较小长度为 L L L bits的分组(packets)
- 在传输速率为
R
R
R 的链路上传输分组
- 举例说明报文交换和分组交换孰优孰劣
- 报文交换
- 分组交换
- 报文交换
明显,分组交换
优势更大。
分组交换的报文交付时间:
例题:
1.3.4 数据交换-报文、分组交换(3)
分组交换
和电路交换
性能孰优孰劣?
- 电路交换:单个用户会独占链路,造成资源浪费。
- 分组交换:采用统计多路复用技术,
允许更多用户同时使用网络,网络资源充分共享。
- 分组交换绝对优于电路交换吗?
- 分组交换适用于
突发
数据传输网络
例如我们下载文件,有时候突然要下载,- 资源充分共享
- 简单、无需呼叫建立(电路交换需要)
- 分组交换
可能产生拥塞(congestion)
:分组延迟和丢失- 需要协议处理可靠数据传输和拥塞控制
- 问题:如何提供电路级性能保障?
例如音视频应用所需的带宽保障。
- 分组交换适用于
1 计算机网络概述(下)
1.4 计算机网络性能
1.4.1 速率
速率
即数据率
(data rate)或称数据传输速率
或比特率
(bit rate) 。速率往往是指额定速率或标称速率 。
- 单位时间(秒)传输信息(比特)量
- 计算机网络中最重要的一个性能指标
- 单位:b/s(或bps)、kb/s、Mb/s、Gb/s
- k= 1 0 3 10^3 103、M= 1 0 6 10^6 106、G= 1 0 9 10^9 109
1.4.2 带宽
- “
带宽
”(bandwidth)原本指信号具有的频带宽度, 即最高频率与最低频率之差,单位是赫兹(Hz) - 网络的“带宽”通常是数字信道所能传送的“
最高数据率
”,单位:b/s (bps) - 常用的带宽单位:
- kb/s ( 1 0 3 10^3 103 b/s)
- Mb/s( 1 0 6 10^6 106 b/s)
- Gb/s( 1 0 9 10^9 109 b/s)
- Tb/s( 1 0 1 2 10^12 1012 b/s)
1.4.3 延迟/时延(delay或latency)
问题:分组交换为什么会发生丢包和时延?
回答:分组在路由器缓存中排队
那什么时候会出现分组排队的情况?
分组到达速率超出输出链路容量时
- 等待输出链路可用时
1.4.4 四种分组延迟
-
d
p
r
o
c
d_{proc}
dproc:
结点处理延迟 (nodal processing delay)
- 差错检测
- 确定输出链路
- 通常<msec(延迟时间为毫秒级)
-
d
q
u
e
u
e
d_{queue}
dqueue:
排队延迟 (queueing delay)
- 等待输出链路可用
- 取决于路由器拥塞程度
- R: 链路带宽(bps)
- L: 分组长度 (bits)
- a: 平均分组到达速率
- 流量强度(traffic intensity)= La/R
- La/R ~ 0: 平均排队延迟很小
- La/R -> 1: 平均排队延迟很大
- La/R > 1: 超出服务能力,平均排队延迟无限大!
-
d
t
r
a
n
s
d_{trans}
dtrans:
传输延迟 (transmission delay)
- L: 分组长度(bits)
- R: 链路带宽 (bps)
- d t r a n s d_{trans} dtrans = L/R
-
d
p
r
o
p
d_{prop}
dprop:
传播延迟(propagation delay)
- d: 物理链路长度
- s: 信号传播速度 (~2×108 m/sec)
- d p r o p d_{prop} dprop = d/s
传输延迟和传播延迟的区别:
1.4.5 时延带宽积(单位:bits(比特))
链路的时延带宽积又称为以比特为单位的链路长度
。
1.4.6 分组丢失(丢包)
三大原因:
- 队列缓存容量有限
- 分组到达已满队列将被丢弃 (即丢包)
- 丢弃分组可能由前序结点或源重发(也可能不重发)
1.4.7 吞吐量/率(Throughput)
吞吐量 :表示在发送端与接收端之间传送数据速率 (b/s)
- 即时吞吐量: 给定时刻的速率
- 平均吞吐量 : 一段时间的平均速率
讨论下面情况的吞吐量是多少
吞吐量是 R s R_s Rs还是 R c R_c Rc?
Internet场景的吞吐量
1.5 计算机网络体系结构
1.5.1 计算机网络体系结构概述
为什么需要计算机网络体系结构?
因为计算机网络是一个非常复杂的系统 , 涉及许多组成部分
,需要一个抽象的体系概念将其统一起来。
之所以说计算机网络很复杂,是因为它牵涉到大量硬件和软件,例如:
- 主机(hosts)
- 路由器(routers)
- 各种链路(links)
- 应用(applications)
- 协议(protocols)
- 硬件、软件
- ……
那么问题是:是否存在一种系统结构有效描述计算机网络呢?利用什么样的结构才适合呢?
答案:分层结构
。
复杂系统的分层结构
类比:航空旅行
- 分层结构每层完成一种(类)特定服务/功能
每层依赖底层提供的服务,通过层内动作完成相应功能。(底层提供的服务好比一个接口,其上层来说是透明的,只是给其使用而已。) - 网络体系结构是从
功能上
描述计算机网络结构 - 计算机网络体系结构简称网络体系结构 (network architecture)
是分层结构
- 每层遵循某个/些
网络协议
完成本层功能
由此可以指导协议是按层
使用的。 计算机网络体系结构
是计算机网络的各层及其协议的集合- 体系结构是一个计算机网络的
功能层次及其关系的定义
- 体系结构是
抽象的
只是将计算机网络抽象出来,具体由硬件还是软件实现哪些功能不在考虑范围内。
为何采用分层结构?
- 结构清晰,有利于识别复杂系统的部件及其关系
分层的参考模型(reference model )
- 模块化的分层易于系统
更新
、维护
- 任何一层服务实现的改变对于系统其它层都是透明的
- 例如,登机过程的改变并不影响航空系统的其它部分(层)
- 有利于标准化
将某个分层模型定为一个框架。 - 分层是否有不利之处?
效率较低!例如办某种证件或是申请某些东西,需要一层一层批下来,非常慢!所以会出现“跨层实现”这种处理方式。
分层网络体系结构基本概念
实体(entity)
表示任何可发送或接收信息的硬件或软件进程。- 协议是控制
两个对等实体
进行通信的规则的集合。协议是“水平的
” 。 - 任一层实体需要使用
下层
服务,并遵循本层协议,实现本层功能,向上层
提供服务。服务是“垂直的
”。 - 下层协议的实现对上层的服务用户是
透明
的。 - 同系统的相邻层实体间通过
接口
进行交互,通过服务访问点 SAP (Service Access Point)
,交换原语
,指定请求的特定服务。
1.5.2 OSI参考模型(1)
开放系统互连 (OSI)参考模型是由国际标准化组织 (ISO) 于1984年提出的分层网络体系结构模型,目的是支持异构网络系统
的互联互通 ,它是异构网络系统互连的国际标准
,也是理解网络通信的最佳学习工具
(理论模型) (理论成功,市场失败 )。
7层
(功能),每层完成特定的网络功能
层级 | 作用 | 包含的协议 |
---|---|---|
应用层 | 为特定应用程序提供数据传输服务。 | ( TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet |
表示层 | 对上层信息进行变换,保证一个主机应用层信息被另一个主机的应用程序理解,表示层的数据转换包括数据的加密、压缩、格式转换。 | JPEG、ASCII、GIF、DES、 MPEG |
会话层 | 管理主机之间的会话进程,即负责建立、管理、终止。 | RPC、SQL、NFS |
传输层 | 提供端对端的接口。 | TCP,UDP |
网络层 | 为数据包选择路由。 | IP,ICMP,ARP,RARP) |
数据链路层 | 传输有地址的帧,错误检测功能 | SLIP,CSLIP,PPP,ARP,RARP,MTU |
物理层 | 以二进制数据形式在物理媒体上传输数据。 | ISO2110,IEEE802,IEEE802.2 |
OSI参考模型解释的通信过程
从主机A到主机B的传输过程(注意传输的方向),反之同理。
可以看到不同主机的同一层之间都有协议进行规范和约束,而且中间系统只有物理层
、数据链路层
、网络层
,剩余的4层合称为端-端层(end-end)
。
OSI参考模型数据封装与通信过程
数据无论经过哪一层(除物理层
和数据链路层
之外),都会被该层加一个“某某层头”,加“头”之后称为数据包
,在OSI模型中称之为协议数据单元
。
为什么需要数据封装?
- 增加
控制信息
构造协议数据单元 (PDU) 。“某某头”中包含有控制信息。 控制信息
主要包括:地址(Address)
: 标识发送端/接收端差错检测编码(Error-detecting code)
: 用于差错检测或纠正协议控制(Protocol control)
: 实现协议功能的附加信 息,如: 优先级(priority)、服务质量(QoS)、 安全控制等
1.5.3 OSI参考模型(2)
本节详细介绍物理层、数据链路层、网络层的功能。(只有数据链路层“加头又加尾”)
物理层功能
- 接口特性
机械特性、电气特性、功能特性、规程特性 - 比特编码
考虑该如何给数据编码进行传输。 - 数据率
- 比特同步
在数据通信中最基本的同步方式就是“比特同步”(bit synchronization)或位同步。比特是数据传输的最小单位。比特同步是指接收端时钟已经调整到和发送端时钟完全一样,因此接收端收到比特流后,就能够在每一个比特的中间位置进行判决。比特同步的目的是为了将发送端发送的每一个比特都正确地接收下来。 - 时钟同步
- 传输模式
- 单工(Simplex)
数据只能单向传输。例如电视,只能接受电视台的信号。 - 半双工(half-duplex)
数据可以交替传输。例如对讲机。 - 全双工(full-duplex)
数据可以同时在双方之间传输。例如手机、微信通话。
- 单工(Simplex)
数据链路层功能
数据链路层处理过的数据称为帧
,并以其作为数据单位。
- 负责
结点-结点(node-to-node)
数据传输 - 组帧(Framing)
通常来说就是给数据“加头加尾”。“头尾”中含有差错检测等信息,但信息会依协议的不同而有所不同。 - 物理寻址(Physical addressing)
在帧头中增加发送端和/或接收端的物理地址标识数据帧的发送端和/或接收端。
在数据中添加源物理地址和目的物理地址是非常重要的,否则可能无法准确发送数据。例如广播通信(所有主机连接在同一条链路上):
- 流量控制(Flow control)
避免淹没接收端。 - 差错控制(Error control)
检测并重传损坏或丢失帧,并避免重复帧 。 - 访问(接入)控制(Access control)
在任一给定时刻决定哪个设备拥有链路(物理介质)控制使用权 。
网络层功能
在同一个网络里,凭数据链路层
的物理地址就可以传输数据,但如果要跨越多个网络进行数据传输则需要加上网络层
的地址。
- 路由(Routing)
- 路由器(或网关)互连网络,并路由分组至最终目的主机
- 路径选择
- 分组转发
其中红色英文字母代表网络层
地址,蓝色数字代表数据链路层
物理地址,经过这样的处理就可以进行跨网络
数据传输了。
1.5.4 OSI参考模型(3)
本节详细介绍4个端-端层
的功能。
传输层功能
负责源-目的(端-端) (进程间) 完整报文
传输 。
- 分段与重组
- SAP寻址 (Sideband Address Port 边带寻址端口)
确保将完整报文提交给正确进程,如端口号 。
- 连接控制
传输层的“连接”并非电路交换中的连接(如打电话📞),这是逻辑
上的连接。 - 流量控制
传输的速率。 - 差错控制
数据的纠正。
会话层功能
- 对话控制(dialog controlling)
建立、维护。 - 同步(synchronization)
在数据流中插入“同步点”。
同步点:
会话层基于传输层所提供的服务,组织和同步进程间的通信,提供会话服务、会话管理和会话同步等功能。其中,所谓同步就是使会话服务用户对会话的进展情况都有一致的了解,在会话被中断后可以从中断处继续下去,而不必从头恢复会话
。换而言之,会话用户把报文分成若干个数据单元,并在相邻的两单元间插入同步点并加以同步编号
。出现问题时,可将会话的状态复位到前一个同步点上
继续进行会话。
- 最“薄”的一层
在现代网络参考模型中一般没有单独的会话层。
表示层功能
处理两个系统间交换信息的语法与语义(syntax and semantics )
问题 。
同网络层
一样,在现代网络参考模型中一般也没有单独的表示层
。
- 数据表示转化
转换为主机独立的编码。例如机器大小端
存储方式。 - 加密/解密
- 压缩/解压缩
应用层功能
- 支持用户通过用户代理(如浏览器)或网络接口
使用网络(服务)
- 典型应用层服务:
- 文件传输(FTP)
- 电子邮件(SMTP)
- Web(HTTP)
- ……
1.5.5 TCP/IP参考模型
Internet采用该参考模型。
TCP必须提供
可靠性
的良好性能,这正是大多数用户所期望的而IP又没有提供的功能。
TCP协议🔗
1.5.6 "5层"参考模型
大部分现代网络采用该参考模型,因为它结合了OSI参考模型
和TCP/IP
参考模型的优点。
应用层
: 支持各种网络应用
FTP, SMTP, HTTP 。传输层
: 进程-进程的数据传输
TCP,UDP。网络层
: 源主机到目的主机的数据分组路由与转发
IP协议、路由协议等 。链路层
: 相邻网络元素(主机、交换 机、路由器等)的数据传输
以太网(Ethernet)、802.11 (WiFi)、 PPP 。物理层
: 比特传输
5层模型的数据封装
注意:
源主机中的帧
按路线第一次到达路由器时会返回到源主机的网络层恢复成数据报
,然后再按路线传输,经过路由器时再变成帧
,然后才传输到目的主机。
1.6 计算机网络发展历史
1.6.1 “1961-1972”: 早期分组交换原理的提出与应用
- 1961: Kleinrock – 排队论证实分组交换的有效性
- 1964: Baran – 分组交换应用于军事网络
- 1967: ARPA(Advanced Research Projects Agency)提出ARPAnet 构想
- 1969: 第一个ARPAnet 结点运行
- 1972:
- ARPAnet公开演示
- 第一个主机-主机协议NCP (Network Control Protocol)
- 第一个e-mail程序
- ARPAnet拥有15个结点
1.6.2 “1972-1980”: 网络互连,大量新型、私有网络的涌现
- 1970:在夏威夷构建了 ALOHAnet卫星网络
- 1974: Cerf 与 Kahn – 提出网络互连体系结构
- 1976: Xerox设计了以太网
以太网🔗 - 70’s后期:
- 私有网络体系结构: DECnet, SNA, XNA
- 固定长度分组交换 (ATM 先驱)
- 1975:ARPAnet移交给美国国防部通信局管理
- 1979: ARPAnet拥有200结点
1.6.3 “1980-1990”: 新型网络协议与网络的激增
- 1983:
部署TCP/IP
- 1982: 定义了smtp电子邮件协议
- 1983: 定义了DNS
- 1985: 定义了FTP协议
- 1988: TCP拥塞控制
- 新型国家级网络:
CSnet, BITnet,NSFnet
, Minitel(法国) - 1986:NSFnet初步形成了一个由骨干网、区域网和校园网组成的三级网络
- 100,000台主机连接公共网络
1.6.4 “1990, 2000’s”: 商业化 , Web, 新应用
- 1990’s早期: ARPAnet退役
- 1991: NSF解除NSFnet的商业应用限制(1995年退役), 由私营企业经营
- 1992:因特网协会
ISOC成立
- 1990s后期:
Web应用
- 超文本(hypertext) [Bush 1945, Nelson 1960’s]
- HTML, HTTP: Berners-Lee
- 1994: Mosaic、Netscape浏览器
- 1990’s后期:Web开始商业应用
- 1990’s后期 – 2000’s:
- 更多极受欢迎的
网络应用
: 即时消息系统(如QQ), P2P文件共享 - 网络安全引起重视
- 网络主机约达50000,
网络用户达1亿
以上 - 网络主干链路带宽达到 Gbps
- 更多极受欢迎的
1.6.5 “2005~今”
- ~7.5亿主机
智能手机和平板电脑 宽带接入
的快速部署- 无处不在的
高速无线接入
快速增长 - 出现
在线社交网络
:
Facebook: 很快拥有10亿用户 - 服务提供商 (如Google, Microsoft)创建其自己的
专用网络
绕开Internet,提供“即时”接入搜索、email等服务 - 电子商务、大学、企业等开始在“
云
”中运行自己的服务 (如, Amazon EC2)
2 应用层
2.1 网络应用(层)内容概述
本讲内容简介:
- 网络应用体系结构
客户机/服务器
:Web、Email等P2P
- 混合结构
- 网络应用的服务需求
- 可靠性
- 带宽
- 时延
- Internet传输层服务模型
- TCP
- UDP
- 特定网络应用及协议
- HTTP
- SMTP,POP,IMAP
- DNS
- P2P应用
- Socket编程
- TCP
- UDP
2.2 网络应用的基本原理
2.2.1 网络应用的体系结构
客户机/服务器结构(Client-Server, C/S)
总而言之,这个结构需要大量主机
(充当服务器)。
- 服务器
7*24小时
提供服务永久性
访问地址/域名- 利用
大量服务器
实现可扩展性
- 客户机
- 与服务器通信,使用服务器提供的服务
间歇性
接入网络- 可能使用动态IP地址
不会与其他客户机直接通信
例子:Web(万维网)
点对点结构(Peer-to-peer, P2P)
总而言之,这个结构不需要大量主机
(充当服务器)。
没有永远在线的服务器
任意端系统/节点之间可以直接通讯
- 节点间歇性接入网络
- 节点可能改变IP地址
- 优点:
高度可伸缩
- 缺点:
难于管理
混合结构(Hybrid)
集C/S结构
和P2P结构
于一身。
举例:
Napster
文件传输
使用P2P结构文件搜索
采用C/S结构——集中式
- 每个节点向中央服务器登记自己的内容
- 每个节点向中央服务器提交查询请求, 查找感兴趣的内容
2.2.2 网络应用进程通信
网络应用的基础:进程间通信
- 进程:
主机上运行的程序
。
- 同一主机上运行的进程之间如何通信?
- 进程间通信机制
- 操作系统提供
- 不同主机上运行的进程间如何通信?
消息交换。
补充:
有的。千万别认为只有C/S结构才有客户机进程/服务器进程之分。
套接字: Socket
本质上是操作系统提供的API
,编写应用层程序也称为网络编程、套接字编程、Socket编程。
- 进程间通信利用socket发送/接收消息实现
- 类似于寄信
- 发送方将消息送到门外邮箱
- 发送方依赖(门外的)传输基础设施将消息传到接收方所在主机,并送到接收方的门外
- 接收方从门外获取消息
- 传输基础设施向进程提供API
- 传输协议的选择
- 参数的设置
如何寻址进程
寻址
是一个在计算机科学中非常重要并且经常出现的概念。
- 不同主机上的进程间通信,那么每个进程必须拥有
标识符
- 如何寻址主机?——IP地址
- Q: 主机有了IP地址后,
是否足以定位进程
? - A: 否。同一主机上可能同时有多个进程需要通信。
- Q: 主机有了IP地址后,
- 端口号/Port number
- 为主机上每个需要通信的进程分配一个端口号
- HTTPServer: 80
- MailServer:25
- 进程的标识符
IP地址+端口号
应用层协议
- 网络应用需遵循应用层协议
- 公开协议
- 由RFC(Request For Comments)定义
若想了解某协议的具体内容,可以去阅读其RFC文档。 - 允许互操作
- HTTP, SMTP, ……
- 由RFC(Request For Comments)定义
- 私有协议
多数P2P文件共享应用程序。
公开协议和私有协议:
公开协议一般都是标准化的协议,可以说是广泛被世人所使用的协议。而私有协议一般是个人制定的,例如商家为保护自己的利益而开发的指定的内部的网络协议。
应用层协议的内容
- 消息的类型(type)
请求消息
响应消息
- 消息的语法(syntax)/格式
消息中有哪些字段(field)?
每个字段如何描述
- 字段的语义(semantics)
字段中信息的含义。
- 规则(rules)
进程何时发送/响应消息
进程如何发送/响应消息
2.2.3 网络应用需求
即网络应用对传输服务的需求。
- 数据丢失(data loss)/可靠性(reliability)
某些网络应用能够容忍一定的数据丢失:网络电话
某些网络应用要求100%可靠的数据传输:文件传输,telnet
- 时间(timing)/延迟(delay)
有些应用只有在延迟足够低时才“有效”
网络电话/网络游戏
- 带宽(bandwidth)
某些应用只有在带宽达到最低要求时才“有效”:网络视频
某些应用能够适应任何带宽——弹性应用:email
典型网络应用对传输服务的需求:
2.2.4 Internet提供的传输(层)服务
-
TCP服务
面向连接
: 客户机/服务器进程间需要建立连接可靠的传输
流量控制
: 发送方不会发送速度过快,超过接收方的处理能力拥塞控制
: 当网络负载过重时能够限制发送方的发送速度不提供时间/延迟保障
不提供最小带宽保障
-
UDP服务
- 无连接
- 不可靠的数据传输
- 不提供:
- 可靠性保障
- 流量控制
- 拥塞控制
- 延迟保障
- 带宽保障
没事,就是想皮一下典型网络应用所使用的传输层服务:
尽管UDP看起来"很无用",但还是大有用处的。
可以查看为什么UDP有时比TCP更有优势?🔗
2.3 Web应用
2.3.1 Web应用概述
Web与HTTP
- World Wide Web: Tim Berners-Lee
- 网页
- 网页互相链接
- 网页(Web Page)包含多个对象(objects)
- 对象:HTML文件、JPEG图片、视频文件、动态脚本等
- 基本HTML文件:包含对其他对象引用的链接
- 对象的寻址(addressing)
- URL(Uniform ResoureLocator):统一资源定位器 RFC1738
统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示
,是互联网上标准资源的地址
。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。它最初是由蒂姆·伯纳斯·李发明用来作为万维网的地址。现在它已经被万维网联盟编制为互联网标准RFC1738了。 - Scheme://host:port/path
协议、主机地址、端口号、对象地址
省略了协议https:
- URL(Uniform ResoureLocator):统一资源定位器 RFC1738
HTTP协议概述
- 万维网应用遵循超文本传输协议(HTTP)
- 超文本传输协议 (HTTP)
HyperText Transfer Protocol C/S结构
- 客户—Browser:请求、接收、展示Web 对象
浏览器。 - 服务器—WebServer:响应客户的请求 ,发送对象
- 客户—Browser:请求、接收、展示Web 对象
- HTTP版本:
- 1.0:RFC 1945
- 1.1:RFC 2068
- 使用TCP传输服务
过程如下
:- 服务器在80端口等待客户的请求
- 浏览器发起到服务器的TCP连接(创建套接字Socket)
TCP是传输层
协议。 - 服务器接受来自浏览器的TCP连接
- 浏览器(HTTP客户端)与Web服务器(HTTP服务器)交换HTTP消息
- 关闭TCP连接
- 无状态(stateless)
服务器不维护任何有关客户端过去所发请求的信息。
为何是无状态呢?
2.3.2 HTTP连接类型
非持久性连接(Nonpersistent HTTP)
- 每个TCP连接最多允许传输一个对象
- HTTP 1.0版本使用非持久性连接
客户机在服务器中获取一个资源的过程:
- RTT(Round Trip Time)
从客户端发送一个很小的数据包到服务器 并返回所经历的时间。
- 响应时间(Response time)
- 发起、建立TCP连接:
1个RTT
- 发送HTTP请求消息到HTTP响应消息的前几个字节到达:
1个RTT
- 响应消息中所含的文件/对象传输时间
Total=2RTT +文件发送时间
- 发起、建立TCP连接:
持久性连接(Persistent HTTP)
- 每个TCP连接允许传输多个对象
- HTTP 1.1版本默认使用持久性连 接
非持久性连接的问题
- 每个对象需要2个RTT
- 操作系统需要为每个TCP连接开销资源(overhead)
- 浏览器会怎么做?
- 打开多个并行的TCP连接以获取网页所需对象
- 给服务器端造成什么影响?
持久性连接
- 发送响应后,服务器保持TCP连接的打开
- 后续的HTTP消息可以通过这个连接发送
无流水(pipelining)的持久性连接
- 客户端只有收到前一个响应后才发送新的请求
- 每个被引用的对象耗时1个RTT
带有流水机制的持久性连接
- HTTP 1.1的默认选项
- 客户端只要遇到一个引用对象就尽快发出请求
- 理想情况下,收到所有的引用对象只需耗时约1个RTT
带流水和不带流水的区别:
当我们发送一个数据包,正常的理解是收到ACK包之后,再发送下一个数据包,这样,一个往返
的时间,我们只传输了一个数据包
,浪费了大量资源。流水线就是我们会依次发送多个数据包
,这样,在第一个数据包得到ACK之前
,我们又发出了几个
,然后再依次接受他们的ACK
,循环往复,大大的提高了网络的传输速率。
计算机学科的所有理论,能通过程序编写出来的才有实用价值,那带流水机制的持久性连接的程序该如何编写呢?
2.3.3 HTTP消息格式
- HTTP协议有两类消息
- 请求消息(request)
- 响应消息(response)
HTTP请求消息
- 请求消息:
ASCII:人直接可读。
HTTP请求消息的通用格式
上传输入的方法
例如在网页上输入自己的QQ账号密码。
POST方法
- 网页经常需要填写表格(form)
- 在请求消息的消息体(entity body) 中上传客户端的输入
URL方法
- 使用GET方法
- 输入信息通过request行的URL字段上传
方法的类型
-
HTTP/1.0
- GET
- POST
- HEAD
请Server不要将所请求的对象放入响应消息中。
-
HTTP/1.1
- GET. POST, HEAD
- PUT
将消息体中的文件上传到URL字段所指定的路径。 - DELETE
删除URL字段所指定的文件。
HTTP响应消息
HTTP响应状态代码
- 响应消息的第一行
- 示例
200 OK
301 Moved Permanently
400 Bad Request
404 Not Found
505 HTTP Version Not Supported
体验一下HTTP
- 利用telnet登录到某个Web服务器(网页,如哈工大官网)
telnet www.hit.edu 80 - 输入一个HTTP请求
GET /about/profile.htm HTTP/1.1
Host: www.hit.edu
- 查看HTTP服务器所返回的响应消息
2.3.4 Cookie技术
为什么需要Cookie?
因为HTTP协议无状态
但很多应用需要服务器掌握客户端的状态,如网上购物。
Cookie技术
- Cookie技术
- 某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
- RFC6265(cookie规范)
- Cookie的组件
- HTTP响应消息的cookie头部行
- HTTP请求消息的cookie头部行
- 保存在客户端主机上的cookie文件,由浏览器管理
- Web服务器端的后台数据库
Cookie的原理
Cookie的作用
- Cookie能够用于:
- 身份认证
“十天内免登陆” - 购物车
- 推荐
- Web e-mail
- …
- 身份认证
- 正由于Cookie的功能,也使其导致了严重的隐私问题。
2.3.5 Web缓存技术/代理服务器技术
- 功能:在不访问服务器的前提下满足客户端的HTTP请求。
- 为什么要发明这种技术?
缩短客户请求的响应时间
减少机构/组织的流量
在大范围内(Internet)实现有效的内容分发
- Web缓存/代理服务器
用户设定浏览器通过缓存进行Web访问
浏览器向缓存/代理服务器发送所有的HTTP请求
如果所请求对象在缓存中,缓存返回对象
否则,缓存服务器向原始服务器发送HTTP 请求,获取对象,然后返回给客户端并保存该对象
Web缓存示例(1):
- 假定:
- 对象的平均大小=100,000 比特 (100kb)
- 机构网络中的浏览器平均每秒有15个到原始服务器的请求
- 从机构路由器到原始服务器的往返延迟=2秒
- 网络性能分析:
- 局域网(LAN)的利用率=15% (15*100000/10M)
- 接入互联网的链路的利用率=100% (15*100000/1.5M)
- 总的延迟=互联网上的延迟+访问延迟+局域网延迟=2秒+几分钟+几微秒
通过这个例子的分析我们可以发现,总的延迟是非常大的,主要是因为接入互联网的链路的利用率=100% 。
Web缓存示例(2):
- 为解决示例(1)中的问题,采取如下操作
提升互联网接入带宽=10Mbps 。
- 网络性能分析:
- 局域网(LAN)的利用率=15%
- 接入互联网的链路的利用率=15%
- 总的延迟=互联网上的延迟+访问延迟+局域网 延迟=2秒+几微秒+几微秒
虽说这样能在一定的程度上解决问题,但成本太高了。
Web缓存示例(3):
重头戏来了。
- 安装Web缓存
- 假定缓存命中率是0.4
- 网络性能分析
- 40%的请求立刻得到满足
- 60%的请求通过原始服务器满足
- 接入互联网的链路的利用率下降到60%,从而访问延迟可以忽略不计,例如10微秒
- 总的平均延迟=互联网上的延迟+访问延迟+局 域网延迟=0.6×2.01秒+0.4×n微秒<1.4秒
可以看到,加了一个Web缓存之后总延迟大大下降了!
但是如何确保cache中的内容都是最新版本呢?这就涉及到接下来要介绍的“条件性GET方法”了。
条件性GET方法
- 目标:
如果缓存有最新的版本,则不需要发送请求对象。 - 缓存:
- 在HTTP
请求消息
中声明所持有版本的日期
- If-modified-since:
- 在HTTP
- 服务器:
- 如果
缓存的版本是最新
的,则响应消息中不包含对象,对象可以从缓存中得到 - HTTP/1.0 304 Not Modified
- 如果
2.4 Email应用
2.4.1 Email应用概述
Email应用的构成
- Email应用的构成组件
- 邮件客户端(user agent)
- 读、写Email消息
- 与服务器交互,收、发Email消息
- Outlook, Foxmail, Thunderbird
- Web客户端
- 邮件服务器 (Mail Server)
- 邮箱:存储发给该用户的Email
- 消息队列(message queue):存储等待发送的Email
SMTP协议(Simple Mail Transfer Protocol)
- 邮件服务器之间传递消息所使用的协议
- 客户端:发送消息的服务器
- 服务器:接收消息的服务器
- 邮件客户端(user agent)
SMTP协议: RFC 2821
-
使用
TCP
进行email消息的可靠传输
-
端口25
-
传输过程的三个阶段
- 握手
- 消息的传输
- 关闭
-
命令/响应交互模式
命令(command): ASCII文本
响应(response): 状态代码和语句
-
Email消息只能包含7位ASCII码
Email应用示例
SMTP交互示例
S:服务器
C:客户端
动手尝试SMTP交互
SMTP协议
- 使用持久性连接
- 要求消息必须由7位ASCII码构成
- SMTP服务器利用CRLF.CRLF(回车换行) 确定消息的结束。
与HTTP对比:
- HTTP: 拉式(pull)
- SMTP: 推式(push)
- 都使用命令/响应交互模式
- 命令和状态代码都是 ASCII 码
- HTTP: 每个对象封装在独立的响应消息中
- SMTP: 多个对象在由多个部分构成的消息中发送
2.4.2 Email消息格式和POP协议
Email消息格式
- SMTP:email消息的传输/交换协议
- RFC 822:文本消息格式标准
- 头部行(header)
- To
- From
- Subject
与SMTP命令不同
- 消息体(body)
- 消息本身
- 只能是ASCII字符
- 头部行(header)
Email消息格式:多媒体扩展
因为邮件只能发送ASCⅡ码,所以发送图片等信息时需要多媒体扩展功能。
- MIME:多媒体邮件扩展RFC 2045, 2056
- 通过在邮件头部增加额外的行以声明MIME的内容类型
- 通过在邮件头部增加额外的行以声明MIME的内容类型
邮件访问协议
- 邮件访问协议:客户端从服务器获取邮件
- POP: Post Office Protocol [RFC 1939]
认证/授权(客户端←→服务器)和下载。- “下载并删除”模式
用户如果换了客户端软件,无法重读该邮件。 - “下载并保持”模式
不同客户端都可以保留消息的拷贝。 - POP3是
无状态
的
- “下载并删除”模式
POP协议使用过程:
- IMAP: Internet Mail Access Protocol [RFC 1730]
比POP3协议更加先进。- 更多功能
- 更加复杂
- 能够操纵服务器上存储的消息
所有消息统一保存在一个地方:服务器
。 - 允许用户利用文件夹组织消息
- IMAP支持跨会话(Session)的用户状态:
- 文件夹的名字
- 文件夹与消息ID之间的映射等
- HTTP:163, QQ Mail等。
在浏览器上发送邮件
使用了HTTP协议。比如我在谷歌浏览器上使用QQ邮箱。
- POP: Post Office Protocol [RFC 1939]
2.5 DNS(域名解析系统)
Domain Name System。
互联网上的核心服务,解决了域名和IP地址之间如何映射
的问题。
2.5.1 DNS概述
DNS:Domain Name System
- 如何识别Internet上的主机/路由器? 可以通过下面两个方法:
- IP地址
- 域名:www.hit.edu
- 问题:域名和IP地址之间如何映射?
- 域名解析系统DNS
- 多层命名服务器构成的分布式数据库
- 应用层协议:完成名字的解析
- Internet核心功能,用应用层协议实现
为什么在应用层呢? - 网络边界复杂
- Internet核心功能,用应用层协议实现
DNS
- DNS服务
- 域名向IP地址的翻译
- 主机别名
- 邮件服务器别名
- 负载均衡:Web服务器
- 问题:为什么不使用集中式的DNS?
- 单点失败问题
若服务器损坏坏会导致整个互联网瘫痪。 - 流量问题
- 距离问题
- 维护性问题
- 单点失败问题
分布式层次式数据库
客户端想要查询www.amazon的IP的过程:
- 根域名服务器:客户端查询根服务器,找到com域名解析服务器
- 顶级域名服务器:客户端查询com域名解析服务器,找到amazon域名解析服务器
- 权威域名服务器:客户端查询amazon域名解析服务器,获得www.amazon的IP地址
-
DNS根域名服务器
- 本地域名解析服务器(严格来说不属于层次体系)无法解析域名时,访问根域名服务器
- 根域名服务器
- 如果不知道映射,访问权威域名服务器
- 获得映射
- 向本地域名服务器返回映射
-
TLD域名解析服务器
顶级域名服务器(TLD, top-level domain): 负责com, org, net,edu等顶级域名和国家顶级域名,例如cn, uk, fr
等- Network Solutions维护com顶级域名服务器
- Educause维护edu顶级域名服务器
-
权威(Authoritative)域名服务器:组织的域名解析服务器,提供组织内部服务器的解析服务
- 组织负责维护
- 服务提供商负责维护
-
本地域名解析服务器
不严格属于层级体系
每个ISP(互联网服务提供商(Internet Service Provider))
都有一个本地域名服务器
默认域名解析服务器。- 当主机进行DNS查询时,查询被发送到本地域名服务器
作为代理(proxy)
,将查询转发给(层级式)域名解析服务器系统。
DNS查询示例:
Cis.poly.edu 的主机想获得 gaia.cs.umass.edu 的IP地址
迭代查询
- 被查询服务器返回域名解析服务器的名字
- “我不认识这个域名,但是你可以问题这服务器”
递归查询
将域名解析的任务交给所联系的服务器
DNS记录缓存和更新
-
域名解析服务器获得"域名—IP"映射,即
缓存
这一映射- 一段时间过后,缓存条目失效(删除)
本地域名服务器一般会缓存顶级域名服务器的映射
因此根域名服务器不经常被访问。
-
记录的更新/通知机制
- RFC 2136
- Dynamic Updates in the Domain Name System (DNS UPDATE)
2.5.2 DNS记录和消息格式
DNS记录
资源记录(RR, resource records)。
- Type=A
- Name: 主机域名
- Value: IP地址
- Type=NS
- Name: 域(edu)
- Value: 该域权威域名解析服务器的主机域名
- Type=CNAME
- Name: 某一真实域名的别名
例如:
www.ibm– servereast.backup2.ibm - Value: 真实域名
- Name: 某一真实域名的别名
- Type=MX
Value是与name相对应的邮件服务器。
DNS协议和消息格式
- DNS协议:
查询(query)和回复(reply)消息
HTTP:请求响应
SMTP:命令相应
DNS:查询回复- 消息格式相同
- 消息头部
- Identification: 16位查询编号,回复使用相同的编号
- flags (待补充)
- 查询或回复
- 期望递归
- 递归可用
- 权威回答
如何注册域名?
2.6 P2P应用
2.6.1 P2P应用:原理与文件分发
- Peer-to-peer
没有服务器
任意端系统之间直接通信
- 节点阶段性接入Internet
- 节点可能更换IP地址
文件分发:客户机/服务器vs. P2P ,到底谁快?
-
C/S
从一个服务器向N个节点分发一个文件需要多长时间?
- 服务器串行地发送N个副本
时间: N F / u s NF/u_s NF/us - 客户机
i
i
i 需要
F
/
d
i
F/d_i
F/di 时间下载
- 服务器串行地发送N个副本
-
P2P
从一个服务器向N个节点分发一个文件需要多长时间?
- 服务器必须发送一个副本
时间: F / u s F/u_s F/us - 客户机 i i i 需要 F / d i F/d_i F/di 时间下载
- 总共需要下载 N ⋅ F N\cdot F N⋅F 比特
- 最快的可能上传速率:
u
s
+
Σ
u
i
u_s + \Sigma u_i
us+Σui
(下载是分开下载的,而上传是一起上传。)
- 服务器必须发送一个副本
直观示例:
2.6.2 文件分发:BitTorrent
BitTorrent 基本原理(1)
- 文件划分为256KB的chunk
- 节点加入torrent
- 没有chunk,但是会逐渐积累
向tracker注册以获得节点清单
,与某些节点( “邻居”)建立连接
下载的同时,节点需要向其他节点上传 chunk
- 节点可能加入或离开
- 一旦节点获得完整的文件,它可能(自私地)离开或(无私地)留下
BitTorrent 基本原理(2)
- 获取chunk
- 给定任一时刻,不同的节点持有文件的不同chunk集合
- 节点(Alice)定期查询每个邻居所持有的chunk列表
- 节点发送请求,请求获取缺失的chunk
稀缺优先 。
- 发送chunk: tit-for-tat
- Alice向4个邻居发送chunk:正在向其发送Chunk,速率最快的4个选择为"top4"
每10秒重新评估top 4。
- Alice向4个邻居发送chunk:正在向其发送Chunk,速率最快的4个选择为"top4"
- 每30秒随机选择一个其他节点,向其发送chunk
- 新选择节点可能加入top 4
- “optimistically unchoke”:
上传速率高,则能够找到更好的交易伙伴,从而更快地获取文件。
“你来我往。”
2.6.3 P2P应用:索引技术
P2P: 搜索信息
- P2P系统的
索引
:
信息到节点位置(IP地址+端口号)的映射 。简单来说就是要找到某一结点及其拥有的文件。 - 文件共享(电驴)
利用索引动态跟踪节点所共享文件的位置
节点需要告诉索引它拥有哪些文件
节点搜索索引,从而获知能够得到哪些文件
- 即时消息(QQ)
索引负责将用户名映射到位置
当用户开启IM应用时,需要通知索引它的位置
节点检索索引,确定用户的IP地址
集中式索引技术
- Napster最早采用这种设计
- 节点加入时,通知中央服务器:
- IP地址
- 内容
- Alice查找“Hey Jude”
- Alice从Bob处请求文件
- 节点加入时,通知中央服务器:
集中式索引的问题
内容和文件传输是分布式的, 但是内容定位是高度集中式的。
- 单点失效问题
- 性能瓶颈
- 版权问题?
2.6.4 洪泛式查询索引技术:Query flooding
使用图遍历算法进行搜索。
- 完全分布式架构
- Gnutella采用这种架构
- 每个节点对它共享的文件进行索引,且只对它共享的文件进行索引
- 查询消息通过已有的TCP连接发送
- 节点转发查询消息
- 如果查询命中,则利用反向路径发回查询节点
覆盖网络(overlay network): Graph
- 节点X与Y之间如果有TCP连接, 那么构成一个边(
边链接的两结点存在直接共享关系
) - 所有的活动节点和边构成覆盖网络
- 边:虚拟链路
- 节点一般邻居数少于10个
2.6.5 层次式覆盖网络索引技术
介于集中式索引和洪泛查询之间的方法。
- 每个节点或者是一个
超级节点
,或者被分配一个超级节点- 节点和超级节点间维持TCP连接
- 某些超级节点对之间维持TCP连接(
边链接的两结点存在直接共享关系
)
- 超级节点负责跟踪子节点的内容
案例:Skype
该应用采用了私有协议,所以它架构是我们猜测的。
- 本质上是P2P的:用户/节点对之间直接通信
- 采用层次式覆盖网络索引技术
- 索引负责维护用户名与IP地址间的映射
- 索引分布在超级节点上
2.7 Socket编程
2.7.1 Socket编程-应用编程接口(API)
网络程序设计接口
底层的编程技术一般为少数人所掌握,因为其更加复杂。
应用编程接口 API
几种典型的应用编程接口
- Berkeley UNIX 操作系统定义了一种 API,称为套接字接口(socket interface),简称
套接字( socket)
。 - 微软公司在其操作系统中采用了套接字接口 API ,形成了一个稍有不同的 API,并称之为 Windows Socket Interface,
WINSOCK
。 - AT&T 为其 UNIX 系统 V 定义了一种 API,简写 为
TLI
(Transport Layer Interface)。
2.7.2 Socket编程-Socket API概述
API 用于应用进程与传输层的信息交互,是操作系统
提供的。
Socket API
- 最初设计
- 面向BSD UNIX-Berkley
- 面向TCP/IP协议栈接口
- 目前
- 事实上的工业标准
- 绝大多数操作系统都支持
- Internet网络应用最典型的API接口
用于客户/服务器(C/S) 通信模型
- 应用进程间通信的抽象机制
如何在服务器端找到想找的接口呢?
-
为使客户机准确寻找到想找的插口,传输层协议给每一个插口都定义了
唯一的编号
。- 标识通信端点(
对外
):
IP地址+端口号 。 - 操作系统/进程如何管理套接字(
对内
):
套接字描述符(socket descriptor
) (小整数 )。
- 标识通信端点(
Socket抽象
- 类似于文件的抽象
在UNIX系统中,Socket是与文件一同被管理的。 - 当应用进程创建套接字时,
操作系统分配一个数据结构
存储该套接字相关信息 - 返回套接字描述符
Socket的结构
-
操作系统已定义结构
sockaddr_in
:
-
使用
TCP/IP协议簇
的网络应用程序声明端点地址变量时,使用结构sockaddr_in
2.7.3 Socket编程-Socket API函数(1)
Socket编程架构(以WinSock为例)
WSAStartup
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
- 使用Socket的应用程序在
使用Socket之前必须首先调用 WSAStartup函数
- 两个参数:
- 第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本
十六进制整数,例如0x102表示2.1版
。 - 第二个参数返回实际的WinSock的版本信息
指向WSADATA
结构的指针
- 第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本、低位字节指明主版本
举例:
使用2.1版本的WinSock的程序代码段wVersionRequested = MAKEWORD( 2, 1 ); err = WSAStartup( wVersionRequested, &wsaData );
WSACleanup
int WSACleanup (void);
- 应用程序在完成对请求的Socket库的使用,
最后要调用WSACleanup函数
- 解除与Socket库的绑定
- 释放Socket库所占用的系统资源
socket
sd = socket(protofamily,type,proto);
- 创建套接字
- 操作系统返回套接字描述符(sd)
- 第一个参数(协议族): protofamily =
PF_INET
(TCP/IP) - 第二个参数(套接字类型):
type =SOCK_STREAM
,SOCK_DGRAM
orSOCK_RAW
(TCP/IP) - 第三个参数(协议号):
0
为默认
举例:
创建一个流套接字的代码段struct protoent *p; p=getprotobyname("tcp"); SOCKET sd=socket(PF_INET,SOCK_STREAM,p->p_proto);
Socket面向TCP/IP的服务(套接字)类型
可以看到SOCK_RAW
是比较特殊的,因为它面向网络层
而不是传输层
。
TCP
:可靠、面向连接、字节流传输、点对点UDP
:不可靠、无连接、数据报传输
Closesocket
int closesocket(SOCKET sd);
- 关闭一个描述符为sd的套接字
- 套接字的关闭 : closesocket
- 如果
多个进程
共享一个套接字,调用closesocket
将套接字引用计数减1,减至0才关闭 - 如果进程中有一个线程调用
closesocket
将其与其他线程共享的套接字关闭时,该进程中的其他线程
也将不能
访问该套接字 。
- 如果
- 返回值:
0
:成功SOCKET_ERROR
:失败
bind
int bind(sd,localaddr,addrlen);
客户程序一般不必调用bind函数 。
- 绑定套接字的本地端点地址
IP地址+端口号 。 - 参数:
- 套接字描述符(sd)
- 端点地址(localaddr)
结构sockaddr_in
- 服务器端调用bind函数需要知道:
- 端口号
- IP地址?
关于IP地址,考虑如下情形:
- 服务器应该绑定哪个地址?
- 解决方案
地址通配符:INADDR_ANY
。
使用地址通配符之后无论选哪个IP地址都可以。
2.7.4 Socket编程-Socket API函数(2)
listen
仅用于服务端 。
int listen(sd,queuesize);
- 置服务器端的流套接字处于监听状态
仅服务器端调用
仅用于面向连接的流套接字
- 设置连接请求队列大小(queuesize)
- 返回值:
0
:成功SOCKET_ERROR
:失败
connect
仅用于客户端 。
connect(sd,saddr,saddrlen);
- 客户程序调用connect函数来使客户套接字(sd)与特定计算机的特定端口(saddr)的套接字(服务)进行连接
仅用于客户端
- 可用于
TCP客户端
也可以用于UDP客户端
调用 Connect 函数。- TCP客户端:建立TCP连接
- UDP客户端:指定服务器端点地址
逻辑上连接,物理上未连接。UDP协议规定了物理上无法连接。
accept
仅用于服务端 。
newsock = accept(sd,caddr,caddrlen);
- 服务程序调用accept函数从处于监听状态的流套接字 sd 的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个
新的套接字
来与客户套接字创建连接通道用于TCP套接字
仅用于服务器
利用新创建的套接字 (newsock)与客户通信
send, sendto
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
send
函数TCP
套接字(客户与服务器)或调用了connect函数的UDP客户端
套接字sendto
函数用于UDP
服务器端套接字与未调用 connect函数的UDP客户端
套接字
recv, recvfrom
recv(sd,*buffer,len,flags);
recvfrom(sd,*buf,len,flags,senderaddr,saddrlen);
- recv函数从
TCP
连接的另一端接收数据,或者从调用了connect函数的UDP客户端
套接字接收服务 器发来的数据 - recvfrom函数用于从
UDP
服务器端套接字与未调用connect函数的UDP客户端
套接字接收对端数据
setsockopt, getsockopt
这两个函数很少使用。
int setsockopt(int sd, int level, int optname, *optval, int optlen);
int getsockopt(int sd, int level, int optname, *optval, socklen_t *optlen);
- setsockopt()函数用来设置套接字sd的选项参数
- getsockopt()函数用于获取任意类型、任意状态套接口的选项当前值,并把结果存入optval
2.7.5 Socket API函数小结
WSAStartup
: 初始化socket库(仅对WinSock)WSACleanup
: 清楚/终止socket库的使用 (仅对WinSock)socket
: 创建套接字connect
:“连接”远端服务器 (仅用于客户端)closesocket
: 释放/关闭套接字
WinSock下函数名是closesocket
,UNIX、Linux下函数名是close
。bind
: 绑定套接字的本地IP地址和端口号(通常客户端不需要调用)listen
: 置服务器端TCP套接字为监听模式,并设置队列 大小 (仅用于服务器端TCP套接字)accept
: 接受/提取一个连接请求,创建新套接字,通过新套接字与客户端交互(仅用于服务器端的TCP套接字)recv
: 接收数据(用于TCP套接字或连接模式的客户端UDP套接字)recvfrom
: 接收数据报(用于非连接模式的UDP套接字)send
: 发送数据(用于TCP套接字或连接模式的客户端 UDP套接字)sendto
:发送数据报(用于非连接模式的UDP套接字)setsockopt
: 设置套接字选项参数getsockopt
: 获取套接字选项参数
2.7.6 关于网络字节顺序
- TCP/IP定义了标准的用于协议头中的二进制整数表示:
网络字节顺序
(network byte order)
不同的机器数据存储方式可能会不同(如大小端),网络字节顺序
是为了消除这种影响而存在的标准。 - 某些Socket API函数的参数需要存储为网络字节顺序(如IP地址、端口号等)
- 可以实现本地字节顺序与网络字节顺序间转换的函数
htons
: 本地字节顺序→网络字节顺序(16bits)ntohs
: 网络字节顺序→本地字节顺序(16bits)htonl
: 本地字节顺序→网络字节顺序(32bits)ntohl
: 网络字节顺序→本地字节顺序(32bits)
编程时要注意网络字节顺序和本地字节顺序的转化。
需要注意某些套接字中的参数是否要求必须为网络字节顺序,当要将这些参数在本地显示给用户时要转化为本地字节顺序。
2.7.7 网络应用的Socket API调用基本流程
以TCP连接为例。
2.7.8 Socket编程-客户端软件设计
解析服务器IP地址
- 客户端可能使用
域名
(如:study.163)或IP地址(如:123.58.180.121)标识服务器 - IP协议需要使用32位二进制IP地址
- 需要将域名或IP地址转换为32位IP地址
- 函数
inet_addr( )
实现点分十进制IP地址到32位IP地址转换 - 函数
gethostbyname( )
实现域名到32位IP地址转换
返回一个指向结构hostent
的指针 。
- 函数
解析服务器(熟知)端口号
- 客户端还可能使用
服务名
(如HTTP)标识服务器端口 - 需要将服务名转换为熟知端口号
调用函数getservbyname( )
:- 返回一个指向结构
servent
的指针
- 返回一个指向结构
解析协议号
- 客户端可能使用
协议名
(如:TCP)指定协议 - 需要将协议名转换为协议号(如:6)
调用函数getprotobyname ( )
实现协议名到协议号的转换 :- 返回一个指向结构
protoent
的指针
- 返回一个指向结构
TCP客户端软件流程
- 确定服务器IP地址与端口号
- 创建套接字
- 分配本地端点地址(IP地址+端口号)
操作系统自己完成。 连接服务器(套接字)
物理上连接。- 遵循应用层协议进行通信
- 关闭/释放连接
UDP客户端软件流程
- 确定服务器IP地址与端口号
- 创建套接字
- 分配本地端点地址(IP地址+端口号)
操作系统自己完成。 指定服务器端点地址,构造UDP数据报
逻辑上连接。- 遵循应用层协议进行通信
- 关闭/释放套接字
客户端软件的实现- connectsock()
设计一个 connectsock
过程封装底层代码
/* consock.cpp - connectsock */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <winsock.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif /* INADDR_NONE */
void errexit(const char *, ...);
/*-------------------------------------------------------
* connectsock - allocate & connect a socket using TCP or UDP
*------------------------------------------------------
*/
SOCKET connectsock(const char *host, const char *service, const char *transport )
{
struct hostent *phe; /* pointer to host information entry */
struct servent *pse; /* pointer to service information entry */
struct protoent *ppe; /* pointer to protocol information entry */
struct sockaddr_in sin; /* an Internet endpoint address */
int s, type; /* socket descriptor and socket type */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
/* Map service name to port number */
if ( pse = getservbyname(service, transport) )
sin.sin_port = pse->s_port;
else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
errexit("can't get \"%s\" service entry\n", service);
/* Map host name to IP address, allowing for dotted decimal */
if ( phe = gethostbyname(host) )
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
else if ( (sin.sin_addr.s_addr = inet_addr(host))==INADDR_NONE)
errexit("can't get \"%s\" host entry\n", host);
/* Map protocol name to protocol number */
if ( (ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket */
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket: %d\n", GetLastError());
/* Connect the socket */
if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==SOCKET_ERROR)
errexit("can't connect to %s.%s: %d\n", host, service,GetLastError());
return s;
}
客户端软件的实现-UDP客户端
设计 connectUDP
过程用于创建连接模式客户端UDP套接字
/* conUDP.cpp - connectUDP */
#include <winsock.h>
SOCKET connectsock(const char *, const char *, const char *);
/*-------------------------------------------------------
* connectUDP - connect to a specified UDP service
* on a specified host
*-----------------------------------------------------
*/
SOCKET connectUDP(const char *host, const char *service )
{
return connectsock(host, service, "udp");
}
客户端软件的实现-TCP客户端
设计 connectTCP
过程,用于创建客户端TCP套接字
/* conTCP.cpp - connectTCP */
#include <winsock.h>
SOCKET connectsock(const char *, const char *, const char *);
/*----------------------------------------------------
* connectTCP - connect to a specified TCP service
* on a specified host
*---------------------------------------------------
*/
SOCKET connectTCP(const char *host, const char *service )
{
return connectsock( host, service, "tcp");
}
客户端软件的实现-异常处理
/* errexit.cpp - errexit */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
/*----------------------------------------------------------
* errexit - print an error message and exit
*----------------------------------------------------------
*/
/*VARARGS1*/
void errexit(const char *format, ..
{ va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
WSACleanup();
exit(1);
}
例1:访问DAYTIME服务的客户端(TCP)
DAYTIME服务
- 获取日期和时间
- 双协议服务(TCP、 UDP),端口号13
- TCP版利用TCP连接请求触发服务
- UDP版需要客户端发送一个请求
/* TCPdtc.cpp - main, TCPdaytime */
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
void TCPdaytime(const char *, const char *);
void errexit(const char *, ...);
SOCKET connectTCP(const char *, const char *);
#define LINELEN 128
#define WSVERS MAKEWORD(2, 0)
/*--------------------------------------------------------
* main - TCP client for DAYTIME service *--------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "daytime"; /* default service port */
WSADATA wsadata;
switch (argc) {
case 1:
host = "localhost"; break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1]; break;
default:
fprintf(stderr, "usage: TCPdaytime [host [port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
TCPdaytime(host, service);
WSACleanup();
return 0; /* exit */
}
/*-----------------------------------------------------
* TCPdaytime - invoke Daytime on specified host and print results
*-----------------------------------------------------
*/
void TCPdaytime(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
SOCKET s; /* socket descriptor */
int cc; /* recv character count */
s = connectTCP(host, service);
cc = recv(s, buf, LINELEN, 0);
while( cc != SOCKET_ERROR && cc > 0)
{
buf[cc] = '\0'; /* ensure null-termination */
(void) fputs(buf, stdout);
cc = recv(s, buf, LINELEN, 0);
}
closesocket(s);
}
例2:访问DAYTIME服务的客户端(UDP)
/* UDPdtc.cpp - main, UDPdaytime */
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
void UDPdaytime(const char *, const char *);
void errexit(const char *, ...);
SOCKET connectUDP(const char *, const char *);
#define LINELEN 128
#define WSVERS MAKEWORD(2, 0)
#define MSG “what daytime is it?\n"
/*--------------------------------------------------------
* main - UDP client for DAYTIME service
*--------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = "localhost"; /* host to use if none supplied */
char *service = "daytime"; /* default service port */
WSADATA wsadata;
switch (argc)
{
case 1:
host = "localhost"; break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1]; break;
default:
fprintf(stderr, "usage: UDPdaytime [host [port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
UDPdaytime(host, service);
WSACleanup();
return 0; /* exit */
}
/*-----------------------------------------------------
* UDPdaytime - invoke Daytime on specified host and print results
*-----------------------------------------------------
*/
void UDPdaytime(const char *host, const char *service)
{
char buf[LINELEN+1]; /* buffer for one line of text */
SOCKET s; /* socket descriptor */
int n; /* recv character count */
s = connectUDP(host, service);
(void) send(s, MSG, strlen(MSG), 0);
/* Read the daytime */
n = recv(s, buf, LINELEN, 0);
if (n == SOCKET_ERROR)
errexit("recv failed: recv() error %d\n", GetLastError());
else
{
buf[cc] = '\0'; /* ensure null-termination */
(void) fputs(buf, stdout);
}
closesocket(s);
return 0; /* exit */
}
2.7.9 Socket编程-服务器软件设计
4种类型基本服务器
循环无连接
(Iterative connectionless) 服务器循环面向连接
(Iterative connection-oriented) 服务器并发无连接
(Concurrent connectionless) 服务器并发面向连接
(Concurrent connection-oriented) 服务器
循环无连接服务器基本流程
- 创建套接字
- 绑定端点地址(
INADDR_ANY
+端口号) 反复
接收来自客户端的请求- 遵循应用层协议,构造响应报文,发送给 客户
数据发送
- 服务器端
不能
使用connect()
函数 - 无连接服务器使用
sendto()
函数发送数据报
获取客户端点地址
- 调用
recvfrom()
函数接收数据时,自动提取
循环面向连接服务器基本流程
- 创建(主)套接字,并绑定熟知端口号;
- 设置(主)套接字为被动监听模式,准备用于服务器;
- 调用
accept()
函数接收下一个连接请求(通过 主套接字),创建新套接字用于与该客户建立 连接; - 遵循应用层协议,反复接收客户请求,构造并发送响应(通过新套接字);
- 完成为特定客户服务后,关闭与该客户之间的连接,返回步骤3.
并发无连接服务器基本流程
- 主线程
1: 创建套接字,并绑定熟知端口号;
2: 反复调用recvfrom()
函数,接收下一个客户请求,并创建新线程处理该客户响应; - 子线程
1: 接收一个特定请求;
2: 依据应用层协议构造响应报文,并调用sendto()
发送;
3: 退出(一个子线程处理一个请求后即终止)。
并发面向连接服务器基本流程
- 主线程
1: 创建(主)套接字,并绑定熟知zi端口号;
2: 设置(主)套接字为被动监听模式,准备用于服务器;
3: 反复调用accept()
函数接收下一个连接请求(通过主套接字),并创建一个新的子线程处理该客户响应; - 子线程
1: 接收一个客户的服务请求(通过新创建 的套接字);
2: 遵循应用层协议与特定客户进行交互;
3: 关闭/释放连接并退出(线程终止)
服务器的实现
- 设计一个底层过程隐藏底层代码:
passivesock()
- 两个高层过程分别用于创建服务器端UDP套接字和TCP套接字(调用
passivesock()
函数):passiveUDP()
passiveTCP()
服务器的实现-passivesock()
/* passsock.cpp - passivesock */
#include <stdlib.h>
#include <string.h>
#include <winsock.h> void errexit(const char *, ...);
/*-----------------------------------------------------------------------
* passivesock - allocate & bind a server socket using TCP or UDP
*------------------------------------------------------------------------
*/
SOCKET passivesock(const char *service, const char *transport, int qlen)
{
struct servent *pse; /* pointer to service information entry */
struct protoent *ppe; /* pointer to protocol information entry */
struct sockaddr_in sin;/* an Internet endpoint address */
SOCKET s; /* socket descriptor */
int type; /* socket type (SOCK_STREAM, SOCK_DGRAM)*/
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
/* Map service name to port number */
if ( pse = getservbyname(service, transport) )
sin.sin_port = (u_short)pse->s_port;
else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
errexit("can't get \"%s\" service entry\n", service);
/* Map protocol name to protocol number */
if ((ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type */
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket */
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket: %d\n", GetLastError());
/* Bind the socket */
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
errexit("can't bind to %s port: %d\n", service,GetLastError());
if (type == SOCK_STREAM && listen(s, qlen) == SOCKET_ERROR)
errexit("can't listen on %s port: %d\n", service,GetLastError());
return s;
}
服务器的实现-passiveUDP()
/* passUDP.cpp - passiveUDP */
#include <winsock.h>
SOCKET passivesock(const char *, const char *, int);
/*-------------------------------------------------------------------------------------
* passiveUDP - create a passive socket for use in a UDP server
*-----------------------------------------------------------------------------------
*/
SOCKET passiveUDP(const char *service)
{
return passivesock(service, "udp", 0);
}
服务器的实现-passiveTCP()
/* passTCP.cpp - passiveTCP */
#include <winsock.h>
SOCKET passivesock(const char *, const char *, int);
/*-----------------------------------------------------------------------------------
* passiveTCP - create a passive socket for use in a TCP server
*-----------------------------------------------------------------------------------
*/
SOCKET passiveTCP(const char *service, int qlen)
{
return passivesock(service, "tcp", qlen);
}
例1:无连接循环DAYTIME服务器
/* UDPdtd.cpp - main, UDPdaytimed */
#include <stdlib.h>
#include <winsock.h>
#include <time.h>
void errexit(const char *, ...);
SOCKET passiveUDP(const char *);
#define WSVERS MAKEWORD(2, 0)
/*------------------------------------------------------------------------
* main - Iterative UDP server for DAYTIME service
*------------------------------------------------------------------------
*/
void main(int argc, char *argv[])
{
struct sockaddr_in fsin; /* the from address of a client */
char *service = "daytime"; /* service name or port number */
SOCKET sock; /* socket */
int alen; /* from-address length */
char *pts; /* pointer to time string */
time_t now; /* current time */
WSADATA wsadata;
switch (argc)
{
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage: UDPdaytimed [port]\n");
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
sock = passiveUDP(service);
while (1)
{
alen = sizeof(struct sockaddr);
if (recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr *)&fsin, &alen) == SOCKET_ERROR)
errexit("recvfrom: error %d\n", GetLastError());
(void) time(&now);
pts = ctime(&now);
(void) sendto(sock, pts, strlen(pts), 0,
(struct sockaddr *)&fsin, sizeof(fsin)); }
return 1; /* not reached */
}
例2:面向连接并发DAYTIME服务器
/* TCPdtd.cpp - main, TCPdaytimed */
#include <stdlib.h>
#include <winsock.h>
#include <process.h>
#include <time.h>
void errexit(const char *, ...);
void TCPdaytimed(SOCKET);
SOCKET passiveTCP(const char *, int);
#define QLEN 5
#define WSVERS MAKEWORD(2, 0)
/*------------------------------------------------------------------------
* main - Concurrent TCP server for DAYTIME service
*------------------------------------------------------------------------
*/
void main(int argc, char *argv[])
{
struct sockaddr_in fsin; /* the from address of a client */
char *service = "daytime"; /* service name or port number*/
SOCKET msock, ssock; /* master & slave sockets */
int alen; /* from-address length */
WSADATA wsadata;
switch (argc)
{
case 1: break;
case 2: service = argv[1]; break;
default: errexit("usage: TCPdaytimed [port]\n");
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
msock = passiveTCP(service, QLEN);
while (1)
{
alen = sizeof(struct sockaddr);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
if (ssock == INVALID_SOCKET)
errexit("accept failed: error number %d\n",GetLastError());
if (_beginthread((void (*)(void *)) TCPdaytimed, 0, (void *)ssock) < 0)
{
errexit("_beginthread: %s\n", strerror(errno));
}
}
return 1; /* not reached */
}
/*----------------------------------------------------------------------
* TCPdaytimed - do TCP DAYTIME protocol
*-----------------------------------------------------------------------
*/
void TCPdaytimed(SOCKET fd)
{
char *pts; /* pointer to time string */
time_t now; /* current time */
(void) time(&now);
pts = ctime(&now
(void) send(fd, pts, strlen(pts), 0);
(void) closesocket(fd); }
3 传输层(上)
3.1 传输层服务
3.1.1 本章学习内容概括
- 理解传输层服务的基本理论和基本机制
- 复用/分用
- 可靠数据传输机制
- 流量控制机制
- 拥塞控制机制
- 掌握Internet的传输层协议
- UDP:无连接传输服务
- TCP:面向连接的传输服务
- TCP拥塞控制
3.1.2 传输层服务概述
- 传输层协议为运行在不同Host上的进程 提供了一种
逻辑通信机制
- 端系统运行传输层协议
发送方
:将应用递交的消息分成一个或多个的Segment,并向下传给网络层。接收方
:将接收到的segment组装成消息, 并向上交给应用层。- 传输层可以为应用提供多种协议
- Internet上的TCP
- Internet上的UDP
3.1.3 传输层 vs. 网络层
- 网络层:提供
主机
之间的逻辑通信机制 - 传输层:提供
应用进程
之间的逻辑通信机制- 位于网络层之上
- 依赖于网络层服务
- 对网络层服务进行(可能的)增强
3.1.4 Internet传输层协议
- 可靠、按序的交付服务(TCP)
- 拥塞控制
- 流量控制
- 连接建立
- 不可靠的交付服务(UDP)
- 基于
“尽力而为(Best-effort)”
的网络层 ,没有做(可靠性方面的)扩展 - 两种服务均不保证
- 延迟
- 带宽
- 基于
3.2 复用和分用
即多路复用和多路分用。
3.2.1 分用如何工作?
- 主机接收到IP数据报(datagram)
- 每个数据报携带源IP地址、目的IP地址。
- 每个数据报携带一个传输层的段(Segment )。
- 每个段携带源端口号和目的端口号
- 主机收到Segment之后,传输层协议提取IP地址和端口号信息,将Segment导向相应的Socket
- TCP做更多处理
3.2.2 无连接分用
-
利用端口号创建Socket
DatagramSocket mySocket1 = new DatagramSocket(99111);
DatagramSocket mySocket2 = new DatagramSocket(99222); -
UDP的Socket用
二元组标识
- (目的IP地址,目的端口号)
-
主机收到UDP段后
- 检查段中的目的端口号
- 将UDP段导向绑定在该端口号的 Socket
-
来自不同源IP地址和/或源端口号的 IP数据包被导向同一个Socket
3.2.3 面向连接的分用
- TCP的Socket用四元组标识
- 源IP地址
- 源端口号
- 目的IP地址
- 目的端口号
- 接收端利用所有的四个值将Segment导向合适的Socket
- 服务器可能同时支持多个TCP Socket
- 每个Socket用自己的四元组标识
- Web服务器为每个客户端开不同的 Socket
3.2.4 面向连接的分用:多线程Web服务器
有时一个进程占用的资源过大,就会将其分成多个线程。
3.3 无连接传输协议-UDP
UDP: User Datagram Protocol [RFC 768]。
多用于容错性较高
的应用软件。
- 基于Internet IP协议
- 复用/分用
- 简单的错误校验
- ·“Best effort”服务·,UDP段可能
- 丢失
- 非按序到达
- 无连接
- UDP发送方和接收方之间不需要握手
- 每个UDP段的处理独立于其他段
- 常用于流媒体应用
- 容忍丢失
- 速率敏感
- UDP还用于
- DNS
- SNMP
- 在UDP上实现可靠数据传输?
- 在应用层增加可靠性机制
- 应用特定的错误恢复机制
3.3.1 UDP校验和(checksum)
目的:检测UDP段在传输中是否发生错误(如位翻转)。
- 发送方
- 将段的内容视为16-bit整数
- 校验和计算:计算所有整数的和 ,进位加在和的后面,将得到的值按位求反,得到校验和
- 发送方将校验和放入校验和字段
- 接收方
- 计算所收到段的校验和
- 将其与校验和字段进行对比
- 不相等:检测出错误
相等:没有检测出错误(但可能有错误)
校验和计算示例
- 注意:
- 最高位进位必须被加进去
- 示例:
3.4 可靠数据传输的基本原理
3.4.1 可靠数据传输概述
什么是可靠?
不错、不丢、不乱。- 可靠数据传输协议
- 可靠数据传输对应用层、传输层、链路层都很重要
- 网络Top-10问题
- 信道的不可靠特性决定了可靠数据传输协议(rdt)的复杂性
可靠数据传输协议基本结构:接口
可靠数据传输协议
可靠数据传输协议——Rdt协议。
- 渐进地设计可靠数据传输协议的发送方和接收方
- 只考虑单向数据传输
- 但控制信息双向流动
- 利用状态机(Finite State Machine, FSM)刻画传输协议
3.4.2 Rdt 1.0:可靠信道上的可靠数据传输
- 底层信道完全可靠
- 不会发生错误(bit error)
- 不会丢弃分组
- 发送方和接收方的
FSM独立
FSM: 有限状态机
有限状态机-Finite State Machine,简写为 FSM,是表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用。通常 FSM 包含几个要素:状态的管理、状态的监控、状态的触发、状态触发后引发的动作。
3.4.3 Rdt 2.0:产生位错误的信道
- 底层信道可能翻转分组中的位(bit)
- 利用
校验和
检测位错误
- 利用
- 如何从错误中恢复?
确认机制(Acknowledgements, ACK)
: 接收方显式地告知发送方分组已正确接收NAK
:接收方显式地告知发送方分组有错误- 发送方收到NAK后,
重传
分组
- 基于这种重传机制的rdt协议称为
ARQ
(Automatic Repeat reQuest)协议 - Rdt 2.0中引入的新机制
- 差错检测
- 接收方反馈控制消息: ACK/NAK
- 重传
FSM规约
无错误场景
有错误场景
3.4.4 Rdt 2.1和2.2
Rdt 2.0有什么缺陷?
- 如果ACK/NAK消息发生错误/被破坏(corrupted)会怎么样?
- 为ACK/NAK增加校验和,检错并纠错
- 发送方收到被破坏ACK/NAK时不知道接收方发生了什么,添加额外的控制消息
- 如果ACK/NAK坏掉,发送方重传
- 不能简单的重传:产生
重复分组
- 如何解决重复分组问题?
序列号(Sequence number)
: 发送方给每个分组增加序列号- 接收方丢弃重复分组
Rdt 2.1: 发送方, 应对ACK/NAK破坏
Rdt 2.1: 接收方, 应对ACK/NAK破坏
没搞懂,后续再补充
Rdt 2.1 vs. Rdt 2.0
发送方
:- 为每个分组增加了序列号
- 两个序列号(0, 1)就够用,为什么 ?
- 需校验ACK/NAK消息是否发生错误
- 状态数量翻倍
- 状态必须“记住”“当前”的分组 序列号
接收方
- 需判断分组是否是重复
- 当前所处状态提供了期望收到分组 的序列号
- 注意:接收方无法知道ACK/NAK是否被发送方正确收到
- 需判断分组是否是重复
3.4.5 Rdt 2.2: 无NAK消息协议
- 我们真的需要两种确认消息(ACK + NAK)吗?
- 与rdt 2.1功能相同,但是只使用ACK
- 如何实现?
- 接收方通过ACK告知最后一个被正确接收的分组
- 在ACK消息中
显式地加入被确认分组的序列号
- 发送方收到重复ACK之后,采取与收到NAK消息相同的动作
- 重传当前分组
FSM片段
没搞懂,后续再补充
3.4.6 Rdt 3.0
如果信道既可能发生错误,也可能丢失分组,怎么办?
“校验和 + 序列号 + ACK + 重传”够用吗?- 方法:发送方等待“
合理
”时间- 如果没收到ACK,重传
- 如果分组或ACK只是延迟而不是丢了
- 重传会产生重复,序列号机制能够处理
- 接收方需在ACK中显式告知所确认的分组
- 需要
定时器
Rdt 3.0发送方FS
Rdt 3.0示例(1)
Rdt 3.0示例(2)
Rdt 3.0性能分析
- Rdt 3.0能够正确工作,但性能很差
- 示例:1Gbps链路,15ms端到端传播延迟,1KB分组
- 发送方利用率:发送方发送时间百分比
一次完整的分组传输往返所耗的时间仅仅只有0.027%是有用的。 - 在1Gbps链路上每30毫秒才发送一个分组→33KB/sec
30ms发送一个1kb分组,1s则发送了33Kb。
难怪我家100M的宽带网速才3M多。。。
- 网络协议限制了物理资源的利用
- 发送方利用率:发送方发送时间百分比
Rdt 3.0: 停等操作
Rdt 3.0 性能如此之差的原因。
3.4.7 流水线机制与滑动窗口协议
- 流水线机制:提高资源利用率
流水线协议
- 允许发送方在收到ACK之前
连续发送
多个分组- 更大的
序列号范围
- 发送方和/或接收方需要更大的存储空间以
缓存分组
- 更大的
滑动窗口协议
滑动窗口协议: Sliding-window protocol
- 窗口
- 允许使用的序列号范围
- 窗口尺寸为N:最多有N个等待确认的消息
- 滑动窗口
- 随着协议的
运行
,窗口在序列号空间内向前滑动
- 滑动窗口协议:
GBN
,SR
- 随着协议的
3.4.8 Go-Back-N(GBN)协议
属于滑动窗口协议之一。
Go-Back-N(GBN)协议: 发送方
-
分组头部包含k-bit序列号
-
窗口尺寸为N,最多允许N个分组未确认
-
ACK(n): 确认到序列号n(包含n)的分组均已被正确接收
- 可能收到重复ACK
-
为空中的分组设置
计时器(timer)
-
超时
Timeout(n)
事件: 重传序列号大于等于n,还未收到ACK的所有分组
GBN: 发送方扩展FSM
Go-Back-N(GBN)协议: 接收方
- ACK机制: 发送拥有最高序列号的、已被正确接收的分组的ACK
- 可能产生重复ACK
- 只需要记住唯一的expectedseqnum
- 乱序到达的分组:
- 直接丢弃 → → →接收方没有缓存
- 重新确认序列号最大的、按序到达的分组
GBN: 接收方扩展FSM
GBN示例
练习题
- 问题:数据链路层采用后退N帧(GBN)协议,发送方已经发送了编号为 0~7的帧。当计时器超时时,若发送方只收到
0、2、3
号帧的确认 ,则发送方需要重发的帧数是多少?分别是那几个帧? - 解:根据GBN协议工作原理,GBN协议的确认是累积确认,所以 此时发送端需要重发的帧数是4个,依次分别是
4、5、6、7
号帧。
3.4.9 Selective Repeat(SR)协议
属于滑动窗口协议之一。
- GBN有什么缺陷?
- 接收方对每个分组单独进行确认
- 发送方只重传那些没收到ACK的分组
- SR的改进
- 设置缓存机制,缓存乱序到达的分组
- 为每个分组设置定时器
接收方也有窗口
Selective Repeat:发送方/接收方窗口
博主觉得下面的图看了也是白看。
SR协议运作方式示例
建议配合视频观看。👉🔗
SR协议的“困境”
3.4.10 可靠数据传输原理与协议回顾
- 信道的(不可靠)特性
- 可靠数据传输的需求
- Rdt 1.0
- Rdt 2.0, rdt 2.1, rdt 2.2
- Rdt 3.0
- 流水线与滑动窗口协议
- GBN
- SR
4 传输层(下)
4.1 面向连接传输协议-TCP
4.1.1 TCP概述
-
TCP段结构
-
TCP: 序列号和ACK
序列号
:- 序列号指的是segment中第一个字节的编号, 而不是segment的编号
加入于1kb数据,以500b为一段共分为2段,第2段的序列号应该是500或501.
- 建立TCP连接时,双方随机选择序列
ACKs
:- 希望接收到的下一个字节的序列号
- 累计确认:该序列号之前的所有字节均已被正确接收到
不是纯粹的GBN
也不是纯粹的SR
。
Q
: 接收方如何处理乱序到达的Segment?- A: TCP规范中没有规定,由TCP的实现者做出决策
4.1.2 TCP可靠数据传输
TCP可靠数据传输概述
- TCP在IP层提供的不可靠服务基础上实现
可靠数据传输服务
- 流水线机制
- 累积确认
- TCP使用单一重传定时器
- 触发重传的事件
- 超时
- 收到重复ACK
- 渐进式
- 暂不考虑重复ACK
- 暂不考虑流量控制
- 暂不考虑拥塞控制
TCP:RTT和超时
-
RTT
问题
:如何设置定时器的超时时间?- 大于RTT ,但是RTT是变化的
- 过短:不必要的重传
- 过长:对段丢失时间反应慢
问题
:如何估计RTT?- SampleRTT: 测量从段发出去到收到ACK的时间
- 忽略重传
- SampleRTT变化
- 测量多个SampleRTT,求平均值 ,形成RTT的估计值 EstimatedRTT
- 测量多个SampleRTT,求平均值 ,形成RTT的估计值 EstimatedRTT
- SampleRTT: 测量从段发出去到收到ACK的时间
-
超时
定时器超时时间的设置:
- EstimatedRTT + “安全边界”
- EstimatedRTT变化大 → → →较大的边界
-
测量RTT的变化值: SampleRTT与EstimatedRTT的差值
-
定时器超时时间的设置:
TCP发送方事件
-
从应用层收到数据
- 创建Segment
- 序列号是Segment第一个字节的编号
- 开启计时器
- 设置超时时间:
- TimeOutInterval
-
超时
- 重传引起超时的Segment
- 重启定时器
-
收到ACK
- 如果确认此前未确认的Segment •
- 更新SendBase
- 如果窗口中还有未被确认的分组, 重新启动定时器
- 如果确认此前未确认的Segment •
TCP发送端程序的伪代码
TCP重传示例
第三种情况可以通过重传收到ACK100。
TCP-各种需重传场景下ACK该如何生成
快速重传机制
先保留一下这个说法:TCP有快速重传机制,UDP没有快速重传机制。
- TCP的实现中,如果发生超 时,超时时间间隔将重新设 置,即将超时时间间隔加倍 ,导致其
很大
- 重发丢失的分组之前要等待很 长时间
- 通过重复ACK检测分组丢失
- Sender会背靠背地发送多个分 组
- 如果某个分组丢失,可能会引 发多个重复的ACK
- 如果sender收到对同一数据的 3个ACK,则假定该数据之后 的段已经丢失
快速重传
:在定时器超时之前即 进行重传
快速重传算法
4.1.3 TCP流量控制
-
接收方为TCP连接分配buffer
上层应用有可能接收不来缓存中的数据(上层应用可能处理 buffer中数据的速度较慢)。
-
流量控制的作用
发送方不会传输的太多 、太快以至于淹没接收方 (buffer溢出) 。 -
TCP流量控制其实就是速度匹配机制
发送的速度和接收的速度要匹配好。
TCP流量控制原理详述
Buffer中的可用空间(spare room)
= RcvWindow
= RcvBuffer-[LastByteRcvd-LastByteRead]- Receiver通过在 Segment 的头部字段将
RcvWindow
告诉Sender - Sender限制自己已经发送的但还未收到ACK的数据不超过接收方的空闲 RcvWindow尺寸
- Receiver告知Sender RcvWindow=0,会出现什么情况?
会出现死锁。
解决办法: 就算spare room
已经为0,sender还是要占点空间告诉receiver现在RecWindow
有多大,如果RecWindow
已经不是0
的话receiver就可以接收buffer了。
4.1.4 TCP连接管理
-
TCP sender和receiver在传输数 据前需要建立
连接
-
初始化TCP变量
- Seq. #
- Buffer和流量控制信息
-
Client:连接发起者
Socket clientSocket = new Socket(“hostname”,“port number”); -
Server: 等待客户连接请求
Socket connectionSocket = welcomeSocket.accept(); -
TCP连接管理:建立
TCP的三次握手机制
Step 1
: client host sends TCP SYN segment to server- specifies initial seq #
- no data
Step 2
: server host receives SYN, replies with SYNACK segment- server allocates buffers
- specifies server initial seq. #
Step 3
: client receives SYNACK, replies with ACK segment, which may contain dat
TCP连接管理:关闭
client closes socket: clientSocket.close();Step 1
:client
向server发送TCP FIN 控制segmentStep 2
:server
收到FIN, 回复ACK. 关闭连接, 发送 FIN.Step 3
:client
收到FIN, 回复ACK.- 进入“等待” –如果收到FIN,会重新发送ACK
Step 4
:server
收到ACK. 连接关闭.
TCP连接管理模型
4.2 拥塞控制原理
拥塞(Congestion)
- 非正式定义:“太多发送主机发送了太多数据或者发送速度太快 ,以至于网络无法处理”
- 表现:
- 分组丢失(路由器缓存溢出)
- 分组延迟过大(在路由器缓存中排队)
- 拥塞控制 vs. 流量控制
拥塞控制
与流量控制
是不同的概念。拥塞控制
好比是对社会中所有
交通要道的车流量控制,流量控制
好比是对一条马路
的车流量控制。 - A top-10 problem
4.2.1 拥塞控制原理(1)
拥塞的成因和代价:场景1
- 场景配置
- 两个senders,两个 receivers
- 一个路由器, 无限缓存
- 没有重传
- 场景1下的性能
- λ i n \lambda_{in} λin:发送速率
- λ o u t \lambda_{out} λout:接收速率
- C:带宽
拥塞的成因和代价:场景2
-
场景配置
- 一个路由器, 有限buffers
- Sender重传分组
-
场景2下的性能
- 情况a: Sender能够通过某种机制获知路由器buffer信息,有空闲才发:
λ i n = λ o u t \mathbf{\lambda_{in}=\lambda_{out}} λin=λout
- 情况b: 丢失后才重发:
λ i n ′ > λ o u t \mathbf{\lambda'_{in}>\lambda_{out}} λin′>λout
- 情况c:分组丢失和定时器超时后都重发
λ
i
n
′
\lambda'_{in}
λin′ 变得更大
λ i n ′ > λ i n > λ o u t \mathbf{\lambda'_{in}>\lambda_{in}>\lambda_{out}} λin′>λin>λout
- 情况a: Sender能够通过某种机制获知路由器buffer信息,有空闲才发:
情况
b
和c
都是由于分组丢失需要重传而降低了带宽的利用率。对于给定的”goodput”,要做更多的工作 (重传) ,故造成资源的浪费。这是拥塞的其中一个代价。
拥塞的成因和代价:场景3
-
场景配置
- 四个发送方
- 多跳
- 超时/重传
-
场景3下的性能
假设D发送分组给B,A发送分组给C,此时在路由R2处就会出现资源的争夺。并且,假如A发送给C的分组成功经过了R1,但在R2处丢失了,那么此分组在R1处的处理也被浪费了。当分组被drop时,任何用于该分组的“上游”传输能力全都被浪费掉。
这是拥塞的另一个代价。
4.2.2 拥塞控制原理(2)
拥塞控制的方法
- 端到端拥塞控制:
- 网络层不需要显式的提供支持
- 端系统通过观察loss,delay等网络行为判断是否发生拥塞
TCP采取这种方法
- 网络辅助的拥塞控制:
- 路由器向发送方显式地反馈网络拥塞信息
- 简单的拥塞指示(1bit):SNA, DECbit, TCP/IP ECN, ATM)
- 指示发送方应该采取何种速率
案例:ATM ABR拥塞控制
-
ABR:available bit rate
- “弹性服务”
- 如果发送方路径 “underloaded”
使用可用带宽 。 - 如果发送方路径拥塞
将发送速率降到最低保障速率。。
-
RM(resource management) cells
- 发送方发送
- 交换机设置RM cell位(网络辅助)
NI
bit: rate不许增长CI
bit: 拥塞指示
- RM cell由接收方返回给发送方
-
在RM cell中有显式的速率(ER)字段:两个字节
- 拥塞的交换机可以将ER置为更低的值
- 发送方获知路径所能支持的最小速率
-
数据cell中的EFCI位: 拥塞的交换机将其设为1
- 如果RM cell前面的data cell的EFCI位被设为1,那么发送方在返回的RM cell中置CI位
4.3 TCP拥塞控制及TCP性能分析
4.3.1 TCP拥塞控制
-
Sender限制发送速率
-
CongWin:
- 动态调整以改变发送速率
- 反映所感知到的网络拥塞
-
问题:如何感知网络拥塞?
- Loss事件=timeout或3个重复 ACK
- 发生loss事件后,发送方降低速率
-
如何合理地调整发送速率?
- 加性增—乘性减:
AIMD
- 慢启动:
SS
- 加性增—乘性减:
加性增—乘性减: AIMD
原理
:逐渐增加发送速率,谨慎探测可用带宽,直到发生loss- 方法: AIMD
- Additive Increase:
每个RTT将CongWin增大一个MSS
——拥塞避免 - Multiplicative Decrease:
发生loss后CongWin减半
- Additive Increase:
TCP慢启动: SS
- TCP连接建立时, CongWin=1
- 例:MSS=500 byte, RTT=200msec
- 初始速率=20kbps
- 可用带宽可能远远高于初始速率:
- 希望快速增长
原理
:- 当连接开始时,
指数性增长
指数性增长
(算法如上图)- 每个RTT将CongWin翻倍
- 收到每个ACK进行操作
- 初始速率很慢,但是快速攀升
- 当连接开始时,
- 希望快速增长
Threshold变量
慢启动SS中的指数何使转换为线性增长呢?引入Thewshold变量
。
Q
:何时应该指数性增长切 换为线性增长(拥塞避免 )?A
: 当CongWin达到Loss事件前值的1/2时.
实现方法
:
- 变量
Threshold
- Loss事件发生时, Threshold 被设为Loss事件前
CongWin
值的1/2。
Loss事件的处理
- 3个重复ACKs:
- CongWin切到一半
- 然后线性增长
- Timeout事件:
- CongWin直接设为1个MSS
- 然后指数增长
- 达到threshold后, 再线性增长
TCP拥塞控制: 总结
一
- When CongWinis below Threshold, sender in
slow-start
phase, window grows exponentially. - When CongWinis above Threshold, sender is in
congestion-avoidance
phase, window grows linearly. - When a
triple duplicate ACK
occurs, Thresholdset to CongWin/2and CongWinset to Threshold. - When
timeout
occurs, Thresholdset to CongWin/2and CongWinis set to 1 MSS.
二
4.3.2 TCP性能分析
TCP throughput: 吞吐率
- 给定拥塞窗口大小和RTT,TCP的平均吞吐率是多少?
- 忽略掉Slow start
- 假定发生超时时CongWin的大小为
W
,吞吐率是W/RTT
- 超时后,CongWin=
W/2
,吞吐率是W/2RTT
- 平均吞吐率为:0.75W/RTT
未来的TCP
-
举例:每个Segment有1500个byte, RTT是100ms,希望获得 10Gbps的吞吐率
- throughput = WMSS8/RTT, 则
W=throughput * RTT/(MSS*8) - throughput=10Gbps, 则W=83,333
- throughput = WMSS8/RTT, 则
-
窗口大小为83,333
-
吞吐率与丢包率(loss rate, L)的关系
- CongWin从W/2增加至W时出现第一个丢包,那么一共发送的分组数为 W/2+(W/2+1)+(W/2+2)+….+W = 3W2/8+3W/4
- W很大时,3W2/8>>3W/4,,因此L ≈ 8/(3W2)
W = 8 3 L T h r o u g h p u t = 0.75 ⋅ M S S ⋅ 8 3 L R T T ≈ 1.22 ⋅ M S S R T T L W=\sqrt{\dfrac{8}{3L}} \quad Throughput=\frac{0.75\cdot MSS\cdot \sqrt{\frac{8}{3L}}}{RTT}\approx \frac {1.22\cdot MSS}{RTT\sqrt L} W=3L8Throughput=RTT0.75⋅MSS⋅3L8 ≈RTTL 1.22⋅MSS
-
L = 2·10 - 10
Wow!!! -
高速网络下需要设计新的TCP
TCP的公平性
-
公平性?
- 如果K个TCP Session共享相同的瓶颈带宽R,那么每个Session的平均速率为R/K
- 如果K个TCP Session共享相同的瓶颈带宽R,那么每个Session的平均速率为R/K
-
TCP具有公平性吗
?
是的
-
TCP的公平性
公平性与UDP
- 多媒体应用通常不使用TCP, 以免被拥塞控制机制限制速率
- 使用UDP:以恒定速率发送, 能够容忍丢失
产生了不公平
- 研究:TCP friendly
-
公平性与并发TCP连接
- 某些应用会打开多个并发连接
- Web浏览器
产生公平性问题
-
例子:链路速率为R,已有9个连接
- 新来的应用请求1个TCP,获得 R/10的速率
- 新来的应用请求11个TCP,获得 R/2的速率
研究
TCP friendly
的UDP
是当下的热点之一。
4.4 传输层总结
- 传输层服务的基本原理
复用/解复用
可靠数据传输
流量控制
-拥塞控制
- Internet的传输层
UDP
TCP
- 下一章
离开网络“边界”
进入网络“核心”
参考资料:
哈工大MOOC https://www.icourse163/learn/HIT-154005#/learn/announce
百度百科
网络
声明: 转载请标明来处,创作不易,谢谢。
更多推荐
计算机网络核心知识(上)
发布评论