一. 网络应用的体系结构
典型的网络应用
- 百度,QQ,E-mail,迅雷,支付宝,微信,百度云,淘宝网,网易
- 网络应用有哪些特点
- 与单机应用有哪些本质不同?
- 网络应用应采取什么样的体系结构?
网络应用的体系结构
- 客户机/服务器结构(Client-Server,C/S)
- 点对点结构(Peer-to-Peer,P2P)
- 混合结构(Hybrid)
客户机/服务器结构
- 服务器==>对外提供服务的软件/硬件
- 7*24小时提供服务
- 永久性访问地址/域名
- 利用大量服务器实现可扩展性
- 客户机
- 与服务器通信,使用服务器提供的服务
- 间歇性接入网络
- 可能使用动态IP地址
- 不会与其他客户机直接通信
- 例子:Web
纯P2P结构
- 没有永久在线的服务器
- 任意端系统/节点之间可以直接通讯
- 节点间歇性接入网络
- 节点可能改变IP地址
- P2P相较于C/S优缺点
- 优点==>高度可伸缩
- 缺点==>难于管理
混合结构
- question:
- 能否将两种结构混合在一起使用?
- 混合能够利用两者的优点同时规避两者的缺点吗?
- example: Napster
- 文件传输使用P2P结构
- 文件搜索采用C/S结构——集中式
- 每个节点向中央服务器登记自己的内容
- 每个节点向中央服务器提交查询请求,查找感兴趣的内容
思考
- 为每种体系结构找出5钟以上的网络应用
- 从多个反面/角度对比三种体系结构的优缺点
二. 网络应用进程通信
进程间通信——网络应用的基础
- 进程==>主机上运行的程序
- 同一主机上运行的进程之间如何通信?
- 进程间通信机制
- 操作系统提供
- 不同主机运行的进程间如何通信?
- 消息交换/报文交换
- 客户机进程==>发起通信的进程
- 服务器进程==>等待通信请求进程
- 采用P2P架构的应用是否存在客户机进程/服务机进程之分?==>有
- 同一主机上运行的进程之间如何通信?
- 套接字:Socket : 传输层与应用层数据传输通道
- 进程间通信利用socket发送/接收消息实现
- 类似于寄信
- 发送方将消息送到门外邮箱
- 发送发依赖(门外的)传输基础设施将消息传到接收方所在主机,并发送到接收方门外
- 接收方从门外获取消息
- 传输基础设施向进程提供API
- 传输协议选择
- 参数设置
进程寻址——IP PN
- 不同主机的进程间通信,那么每个进程必须拥有标识符
- 如何寻址主机?——IP地址==>唯一标识internet上的一台主机
- question:主机有了IP地址后,是否足以定位进程?
- answer:否,同一主机上可能同时拥有多个进程需要通信
- 端口号(Port number)
- 为主机上每一个需要通信的进程分配一个端口号
- HTTP Server:80
- Mail Server:25
- 进程的标识符==>IP地址 端口号
应用层协议
网络应用需要遵循的应用层协议
- 公开协议
- 又RFC(Requset For Comments)定义
- 允许互操作
- HTTP,SMTP,…
- 私有协议
- 多数P2P文件共享应用
- 应用层协议内容
- 消息类型(type)
- 请求信息
- 响应信息
- 消息的语法(syntax)/格式
- 消息中有哪些字段(field)?
- 每个字段如何描述
- 字段的语义(semantics)
- 字段中信息的含义
- 规则(rules)
- 进程何时发送/响应信息
- 进程如何发送/响应信息
- 消息类型(type)
网络应用对传输服务的需求
- 数据丢失(data loss)/可靠reliability
- 某些网络应用能够容忍一定的数据丢失:网络电话
- 某些网络应用要求100%可靠的数据传输:文件传输,telnet
- 时间(timing)/延迟(delay)
- 有些应用只有在延迟足够低时才"有效"
- 网络电话/网络游戏
- 带宽(bandwidth)
- 某些应用只有在带宽达到最低要求时才"有效":网络视频
- 某些应用能够适应任何带宽——弹性应用:email
- 典型网络应用对传输服务的需求
Internet提供的传输服务
- TCP服务
- 面向连接:客户机/服务机进程间需要建立连接(全双工通讯)
- 可靠的传输
- 流量控制:发送方不会发送速度过快,超过接收方的处理能力
- 拥塞控制:当网络负载过重时能够限制发送方的发送速度
- 不提供时间/延迟保障
- 不提供最小带宽保障
- UDP服务
- 面向无连接
- 不可靠的数据传输
- 不提供
- 可靠性保障
- 流量控制
- 拥塞控制
- 延迟保障
- 带宽保障
- 典型网络应用所使用的传输层服务
小练习
- 盘点个人PC上所有网络应用,制作一个清单,包含网络应用的名字,功能,协议等
- 基于上述清单,制作表格,分析这些网络应用对传输服务的需求
- 分析这些网络应用所使用的传输服务是TCP还是UDP
三. Web应用
Web与HTTP
- World Wide Web :Tim Berners-Lee 作者
- 网页==>组成单位
- 网页相互连接==>基本结构
- 网页(Web Page)包含多个对象(objects)
- 对象:HTML文件,JPEG图片,视频文件,动态脚本等
- 基本HTML文件:包含对其他对象的引用链接
- 对象的寻址(addressing)
- URL(Uniform Resource Locator):统一资源定位器 RFC1738
- Sheme://host:port/path
HTTP协议概述
- 万维网遵循超文本传输协议HTTP(HyperText Transfer Protoco)协议
- C/S结构
- 客户端——Browser : 请求,接收,展示Web对象
- 服务器——Web Server : 响应客户的请求,发送对象
- HTTP版本:
- 1.0 : RFC 1945
- 1.1 : RFC 2068
- 使用TCP传输服务
- 服务器在80端口等待客户请求
- 浏览器发起到服务器的TCP连接(创建套接字Socket)
- 服务器接收来自浏览器的TCP连接
- 浏览器(HTTP客户端)与Web服务器(HTTP服务器)交换HTTP消息
- 关闭TCP连接
- HTTP协议是无状态协议(stateless)
- 服务器不维护任何有关客户端过去所发请求的信息
- example : 2分钟之前请求百度的首页,2分钟之后再次请求,不会因为2分钟前请求过而不给新的页面,每次请求都是新的响应结果
- 有状态协议的弊端
- 协议更加复杂–>需要维护状态(历史信息)
- 当客户或服务器失效,会产生状态不一致,解决此类不一致代价高
HTTP连接的两种类型
非持久性连接(Nonpersistent HTTP)
- 每个TCP连接最多允许传输一个对象
- HTTP1.0版本使用非持久性连接
- 响应时间分析与建模
- RTT(Round Trip Time)往返传播时延
- 从客户端发送一个很小的数据包到服务器并返回所经历的时间
- 响应时间(Response time)
- 发起,建立TCP连接 : 1个RTT
- 发送HTTP请求消息到HTTP响应消息的前几个字节到达:1个RTT
- 响应消息中所含文件/对象的传输时间
- total = 2RTT 文件发送时间
- RTT(Round Trip Time)往返传播时延
持久性链接(Persistent HTTP)
- 每个TCP连接允许传输多个对象
- HTTP1.1版本默认使用持久性连接
- 非持久性连接的弊端
- 每个对象需要2个RTT
- 操作系统需要为每个TCP连接开销资源(overhead)
- 浏览器执行
- 打开多个并行的TCP连接以获取网页所需对象
- 给服务器造成严重的负担
- 持久性连接的基本思想
- 发送响应后,服务器保持TCP连接打开
- 后续的HTTP消息可以通过这个连接发送
- 无流水(pipelinling)的持久性连接
- 客户端只有收到前一个响应后才发送新的请求
- 每个被引用的对象耗时1个RTT
- 带有流水机制的持久性连接
- HTTP1.1的默认选项
- 客户端只要遇到一个引用对象就尽快发出请求
- 理想情况下,收到所有的引用对象只需耗时约1RTT
消息格式
- HTTP协议有两类消息
- 请求消息(request)
- 响应消息(response)
HTTP请求消息
- ASCII:人直接可读
- 解读一个请求信息
- 请求行(request line)
- 请求方法 URL HTTP版本
- 头部行(header lines)
- Host:声明访问的主机
- question : 传输层TCP协议已经声明了目的主机,此处因何还需声明主机?
- answer : 此处声明的目的主机在缓存和代理服务器时会使用到,否则此处多余
- User-agent:声明代理浏览器版本
- Connection:声明连接完成后的动作
- Accept-language:声明支持的语言
- Host:声明访问的主机
- 空行
HTTP请求消息的通用格式
上传输入的方法
:::info
- POST方法
- 网页经常需要填写表格(form)
- 在请求消息的消息体(entity body)中上传客户端资料 ::: :::info
- URL方法
- 使用GET方法
- 输入信息通过request行的URL字段上传 :::
方法类型
:::info
- HTTP/1.0
- GET
- POST
- HEAD
- 请Server不要将对象放置到响应消息中 ::: :::info
- HTTP/2.0
- GET,POST,HEAD
- PUT
- 将消息中的文件上传到URL字段所指定的路径
- DELETE
- 删除URL字段所指定的文件 :::
HTTP响应消息
- status line 状态行
- 协议及版本 状态代码 状态字符串
- header lines
- Date : 响应时间
- Server : 声明服务器软件
- Last-Modified : 上次修改时间
- Context-Length : 响应内容长度
- Context-Type : 响应类型
响应状态码
- 位于响应消息的第一行
- HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。响应分为五类:
- 信息响应(100–199) : 信息,服务器收到请求,需要请求者继续执行操作
- 成功响应(200–299) : 成功,操作被成功接收并处理
- 重定向(300–399) : 重定向,需要进一步的操作以完成请求
- 客户端错误(400–499) : 客户端错误,请求包含语法错误或无法完成请求
- 服务器错误 (500–599) : 服务器错误,服务器在处理请求的过程中发生了错误
体验HTTP
- 利用Telnet登录到某个服务器
- 开启telnet服务
- 在DOS中输入 : Telnet 空格 要访问的服务器或电脑ip地址 空格 端口号
- 输入一个HTTP请求
- GET /about/profile.htm HTTP/1.1
- Host:www.hit.edu.cn
- 查看HTTP服务器所返回的响应消息
- 输入一个HTTP请求
Cookie技术 (功能拓展)
- 为什么要使用Cookie?
- HTTP协议无状态
- 很多应用需要服务器掌握客户端的状态
- 网上购物等
- 什么是Cookie——Cookie技术
- 某些网站为了辨别用户身份,进行session跟踪而存储在用户本地终端上的数据(通常经过加密)
- RFC6265
- Cookie的组件
- HTTP响应消息的cookie头部件
- HTTP请求消息的cookie头部件
- 保存在客户端主机上的cookie文件,由浏览器管理
- Web服务器端的后台数据库
- Cookie的原理
- Cookie的作用——应用于
- 身份认证
- 购物车
- 推荐
- Web e-mail
- etc…
- 隐私问题
- 思考
- Cookie能够被用于收集隐私?
- 能够收集哪些隐私?
- 你在上网的时候能感觉到自己的隐私被严重侵犯吗?
Web缓存/代理服务器技术(性能拓展)
- 作用
- 在不访问服务器的前提下满足客户端的HTTP请求
- 为什么要发明这种技术?
- 缩短客户请求的响应时间
- 减少机构/组织的流量
- 在大范围内(Internet)实现有效的内容分发CDN(Content Delivery Network,即内容分发网络)
- 技术要点==>在客户端和服务器之间架设一台代理服务器,用于数据缓存和请求响应代理
- 用户向设定浏览器通过缓存进行Web访问
- 浏览器向缓存/代理服务器发送所有的HTTP请求
- 若请求对象在代理服务器缓存中,缓存返回对象
- 否则,缓存服务器向原始服务器发送HTTP请求,获取对象,然后返回给客户端并保存该对象
- 代理服务器即充当客户端,也充当服务器
- 一般由ISP(Internet服务提供商)架设
Web缓存示例
- 已知条件
- 机构内部局域网速率10Mbps
- 接入互联网接入速率1.5Mbps
- 假定
- 每个Web对象的平均大小=100,000比特
- 机构网络中的浏览器平均每秒就有15个到原始服务器的请求
- 从机构路由器到原始服务器的往返延迟RTT=2sec
- 网络性能分析
- 局域网(LAN)的利用率=1.5/10=15%
- 接入互联网的链路利用率=1.5/1.5=100%,网络延迟太大
- 总的延迟=互联网上面的延迟 访问延迟 局域网延迟 = 2sec 几分钟 几微秒
对于接入互联网链路利用率高导致网络延迟
- 解决方案1
- 提升互联网接入带宽=10Mbps
- 网络性能分析
- 局域网(LAN)的利用率=1.5/10=15%
- 接入互联网的链路利用率=1.5/10=15%
- 总的延迟=互联网上面的延迟 访问延迟 局域网延迟 = 2sec 几微秒 几微秒
- 问题==>成本太高
- 解决方案2
- 安装Web缓存
- 假定缓存命中率是0.4
- 网络性能分析
- 40%的请求立刻得到满足
- 60%的请求通过原始服务器满足
- 接入互联网的链路的利用率下降到60%,从而其延迟可以忽略不计,例如:10微秒
- 总的平均延迟=互联网上的延迟 访问延迟 局域网延迟=0.62.01秒 0.4n微秒<1.4秒
条件性GET方法==>维持缓存服务器与远端服务器数据一致
- 目标:
- 如果缓存有最新版本,则不需要发送请求对象
- 缓存:
- 在HTTP请求消息中声明所持有的版本的日期
- if-modified-since :
- 服务器:
- 如果缓存的版本是最新的,则响应消息中不包含对象
- HTTP/1.0 304 Not Modified
- 课后作业
- 检索文献,分析,总结Web技术近年来有哪些新进展?
- 其关键思想和技术是什么,?
四. Email应用
SMTP协议:RFC 2821
- 使用TCP进行email消息的可靠传输
- 端口使用25
- 传输过程的三个阶段
- 握手
- 消息传输
- 关闭
- 命令/响应式交互模式
- 命令(command) : ASCII文本
- 响应(response) : 状态代码和语句
- 使用持久性连接
- SMTP服务器利用CRLF.CRLF确定消息的结束
- Email消息只能包含7位ASCII码
与HTTP对比
- 相同点
- 都使用命令/响应交互模式
- 命令和状态码都是ASCII码
- 不同点
- HTTP
- 本质上是一种拉式(pull)的网络应用(浏览器需要到WebServer端将网页拉回本地)
- 每个对象封装在独立的响应消息中
- SMTP
- 推式(push)网络应用(发送方主动与接收方建立连接,主动推送消息)
- 多个对象在由多个部分构成的消息中发送
- HTTP
Email应用示例
- 发送方和接收方不需要同时在线
SMTP交互示例
手动尝试SMTP交互
- telnet servername 25
- 服务器返回代码220==>连接成功
- 交互命令
- HELO
- MAIL FROM
- RCPT TO
- DATA
- QUIT
思考
- Email作为互联网上的古老应用,从出现至今经过了什么样的演变过程?
- 站在今天的角度看,Email应用有哪些缺点和不足?请查阅资料,给出你的见解。
Email消息格式
- SMTP : email消息的传输/交换协议
- RFC 822 : 文本消息格式标准
- 头部行(header)
- To
- From
- Subject
- 消息体(body)
- 消息本身
- 只能是ASCII字符
多媒体扩展
- MIME : 多媒体邮件扩展 RFC 2045,2056
- 通过在邮件头部增加额外的行以声明MIME的内容类型(ASCII编码传输,使用base64进行编码和解码)
邮件访问协议
- 从服务器获取邮件==>使用邮件访问协议
- POP : Post Office Protocol [RFC 1939]
- 认证/授权(客户端<==>服务器)和下载
- IMAP : Internet Mail Access Protocol [RFC 1730]
- 更多功能
- 更加复杂
- 能够操纵服务器上存储的消息
- HTTP : 163,QQ,Mail等
- POP : Post Office Protocol [RFC 1939]
POP3协议
- 认证过程
- 客户端命令
- User : 声明用户名
- Pass : 声明密码
- 服务器响应
- OK
- -ERR
- 客户端命令
- 事务阶段
- List : 列出消息数量
- Retr : 用编号获取消息
- Dele : 删除消息
- Quit
- "下载并删除"模式
- 用户如果换了客户端软件,无法重读该邮件
- 在办公室使用电脑下载并删除了某个邮件,到家时无法再读取
- 用户如果换了客户端软件,无法重读该邮件
- "下载并保持"模式
- 不同客户端都可以保留消息的拷贝(解决了"下载并删除"模式的问题)
- 产生了新的问题,大量消息保存在邮箱里,早期邮箱容量不足以支持此模式
- 不同客户端都可以保留消息的拷贝(解决了"下载并删除"模式的问题)
- POP3是无状态的
IMAP协议
- 所有消息保存在一个地方==>服务器
- 允许用户利用文件夹组织消息
- IMAP支持跨会话(Session)的用户状态
- 文件夹的名字
- 文件夹与消息ID之间的映射等
- 本质上IMAP是有状态协议
课后练习
- 请查阅资料,比较IMAP与POP3的不同,并调研主流Email服务对IMAP协议的支持情况。
五. DNS应用
DNS概述
- Question : 为什么不使用集中式DNS?——不可伸缩
- 集中式会有产生单点失败问题,导致整个互联网瘫痪
- 流量问题,负载过重
- 距离问题,较远端的客户端访问服务器的RTT太大
- 维护性问题
- 应用层协议 : 完成名字的解析
- Internet核心功能,用应用层协议实现(作为核心功能因何在应用层实现,而不是在更底层实现?)
- 网络边界复杂
- 能够将指定的IP地址映射为域名
- 用途
- 用于解决Internet上主机/路由器的识别问题
- IP地址
- 域名 : www.hit.edu.cn
- 负责域名和IP地址之间的映射,由DNS实现
- 用于解决Internet上主机/路由器的识别问题
- DNS服务
- 域名向IP地址的翻译
- 主机别名
- 邮件服务器别名
- 负载均衡 : Web服务器
- 大型门户网站,对外提供web服务时需要大量服务器(服务器农场),此时可以利用DNS做负载均衡
- 具体的做法 :
- 当进行域名向IP地址翻译时提供多个Web服务器IP地址映射
- 没有新的服务时,进行IP地址调整,使其轮流成为首选IP映射,从而实现负载均衡
- 域名解析过程
DNS(Domain Name System) 域名系统
- 域名解析系统DNS
- 多层命名服务器构成的分布式层次式数据库
- 根域名服务器==>顶级域名服务器==>一级域名服务器
- 客户端想要查询www.baidu.com的IP
- 客户端查询根服务器,找到com域名解析服务器
- 客户端查询com域名解析器,找到baidu.com域名解析服务器
- 客户端查询baidu.com域名解析服务器,获得www.baidu.com的IP地址
- 全球共有13个根服务器,主根服务器和大部分服务器在美国
- 思考
- 我国没有根域名服务器,是否会影响我国的网络安全,会有什么影响
DNS根域名服务器
- 本地域名解析服务器无法解析域名时,访问根域名服务器
- 根域名服务器
- 若不知道映射,访问权威域名服务器
- 获得映射,向本地域名服务器返回映射
顶级域名服务器TLD
- 顶级域名服务器(TLD,Top-level domain)
- 负责com,org,net,edu等顶级域名 和 国家顶级域名 cn,uk,fr,etc…
- Network Solutions维护com顶级域名服务器
- Educause 维护edu顶级域名服务器
- 负责com,org,net,edu等顶级域名 和 国家顶级域名 cn,uk,fr,etc…
权威(Authoritative)域名服务器
- 负责组织的域名解析服,提供组织内部服务器的解析服务
- 组织负责维护
- 服务提供商负责维护
本地域名解析服务器
- 不严格属于层级体系
- 每个ISP有一个本地域名服务器
- 默认域名解析服务器
- 当主机进行DNS查询时,查询被发送到本地域名服务器
- 作为代理(proxy),将查询转发给(层级式)域名解析服务器系统
DNS查询示例
- cis.poly.edu主机想获取gaia.cs.umass.edu的IP地址
- 采用迭代查询
- 被查询服务器返回域名解析服务器的名字
- 递归查询
- 将域名解析的任务交给所联系的服务器
- 例题
DNS记录缓存和更新
- 只要域名解析服务器获得域名——IP映射,即缓存这一映射
- 一段时间之后,缓存条目失效(删除)
- 本地域名服务器一般会缓存顶级域名服务器的映射
- 因此根域名服务器不经常被访问
- 记录的更新/通知机制
- RFC 2136
- Dynamic Updates in the Domain Name System (DNS UPDATE)
DNS记录和消息格式
DNS记录(又称为资源记录)
- 资源记录(RR,Resource records)
- RR format(name,value,type,tll(时间有效性))
- Type A类型
- DNS提供最基本的主机到IP的映射
- Name : 主机域名
- Value : IP 地址
- Type NS类型
- DNS提供域的域名解析器
- Name : 域(edn.cn)
- Value : 该域权威域名解析服务器的主机域名
- Type CHAME类型
- DNS提供域名别名服务
- Name : 某一真实域名的别名
- www.ibm.com —— servereast.backup2.ibm.com
- Value : 真实域名
- Type MX类型
- DNS提供邮件服务器支持
- Value是与name 相对应的邮件服务器
DNS协议与消息
- DNS协议 : 查询(query)和回复(reply)消息
- 消息格式相同
- 消息头部
- identification : 16位查询编号,回复使用相同编号
- flags
- 查询或回复
- 期望递归
- 递归可用
- 权威回答
如何注册域名?
- 例子注册一个域名"networkutopia"
- 在域名管理机构(如 Network Solutions)注册域名networkutopia.com
- 向域名管理机构提供你的权威域名解析服务器的名字和IP地址
- 域名管理机构向com顶级域名服务器中插入两条记录
- 在权威域名解析服务器中
- 为www.networkuptopia.com加入Type A记录,加入Type MX记录
- 思考
- 查阅相关资料,找出那些在应用层实现的Internet核心服务,调研他们的协议,消息格式
六. P2P应用
P2P原理
纯P2P架构
- P2P,即Peer to peer,端到端架构
- 没有服务器,没有永远处于服务的主机
- 任意端系统之间直接通信
- 节点阶段性接入Internet
- 节点可能更换IP地址
P2P应用之文件分发
以文件分发场景为例讲解
- 文件分发 : 客户机/服务器 VS P2P
- 问题描述 : 从一个服务器向N个节点分发一个文件需要多长时间?
- 文件大小F,假定核心网路具有充足带宽(系统性能瓶颈==>服务/节点接入核心网络的带宽)
- Us : 服务器上传带宽
- Ui : 节点的上传带宽
- di : 节点的下载带宽
对于客户机服务器架构
- 服务器串行地发送N个副本
- 时间 : NF/Us
- 客户机i需要F/di时间下载
- 完成分发的总时间
- 总时间较大程度上取决去NF/Us,并且与N大小有关(线性增长关系)
对于P2P架构
- 服务器必须发送一个副本
- 时间 : F/Us
- 客户机i需要F/di时间下载
- 总共需要下载NF比特
- 最快可能上传速率 : Us ∑Ui
- 完成分发的总时间
直观比较C/S与P2P基于文件分发最快传输时间
- 客户端上传速度u,
- F/u=1小时
- Us = 10u
- dmin >= Us
- 小练习
文件发放应用
典型应用 : BitTorrent
节点加入组:torrent, 交换文件快chunk 获取组列表的机器tracker,
- 对于交换同一个文件的文件块,参与的节点会形成一个组(动态变化的)称为一个torrent
- 对于每个torrent有一台机器(tracker)跟踪参与交换文件块(chunk)的节点
- alice向tracker查询torrent有哪些节点,获取节点列表,与其他主机建立TCP连接
- 从连接主机获取文件/发送文件
- 文件划分为256KB的chunk
- 节点加入torrent(组)
- 没有chunk,但是会逐渐积累
- 向tracker注册以获得节点清单,与某些节点(“邻居”)建立连接
- 下载的同时,节点需要向其它节点上传chunk
- 节点可能加入或离开
- 一旦节点获得完整的文件,就可能离开/留下(节点的状态是动态的)
- 获取chunk
- 给定任一时刻,不同的节点持有文件的不同chunk集合
- 结点定期查询每个邻居节点所持有的chunk列表
- 节点发送请求,请求获取缺失的chunk
- 稀缺优先
- 发送chunk : tit-for-tat(一报还一报)
- 节点向3个邻居发送chunk : 正在向其发送chunk,速率最快的3个
- 每10秒重新评估top3(对自己贡献大的)
- 每30秒随机选择一个其它节点,向其发送chunk
- 选择新节点可能加入top3
- “optimistically unchoke”
- 节点向3个邻居发送chunk : 正在向其发送chunk,速率最快的3个
BitTorrent : Tit-for-tat
思考
- BitTorrent技术对网络性能有哪些潜在的危害?
P2P应用之索引技术
搜索信息
- P2P系统的缩索引 : 信息到节点位置(IP地址 端口号)的映射
- 文件共享/分发(电驴/BT)
- 利用索引动态跟踪节点所共享的文件的位置
- 节点需要告诉索引它拥有哪些文件
- 节点搜索索引,从而获知能够得到哪些文件
- 即时消息(QQ)
- 索引负责将用户名映射到位置
- 当前用户开启IM应用时,需要通知索引它的位置
- 节点检索索引,确定用户的IP地址
集中式索引
- Napster最早采用这种设计
- 节点加入时,通知中央服务器
- IP地址
- 内容
- 节点加入时,通知中央服务器
- Alice查找"Hey Jude"
- Alice从Bob处请求文件
- 集中式索引的弊端
- 内容和文件的传输是分布式的,但是内容定位是高度集中式的
- 单点失效问题,一旦中央目录服务器宕机时,整个P2P系统就会崩溃
- 性能瓶颈,负载压力太大
- 版权问题
分布式(洪泛式)查询 : Query Flooding
- 完全分布式架构
- 典型应用 : Gnutella
- 每个节点对它共享的文件进行索引,且只对它共享的文件进行索引
- 覆盖网络(overlay network) : Graph
- 两个节点之间若存在TCP连接,择构成Graph中的一边
- 所有活动节点和边构成覆盖网络(逻辑网络)
- 边 : 虚拟链路
- 节点一般邻居数少于10个
- 查询消息通过已有的TCP连接发送
- 节点转发查询消息
- 如果查询命中,则利用反向路径发回查询节点
- 小练习
层次式覆盖网络
- 介于集中式索引和洪泛查询之间的方法
- 每一个节点或者是一个超级节点,或者被分配一个超级节点
- 节点和超级节点间维持TCP连接
- 某些超级节点对之间维持TCP连接
- 超级节点负责跟踪子节点的内容
- 子节点集中式访问超级节点,超级节点洪泛式查询覆盖网络
P2P应用案例 : Skype
- 本质上是P2P,用户节点对之间直接通信(真正通话时,不走超级节点)
- 私有应用层协议
- 采用层次式覆盖网络架构
- 索引负责维护用户名与IP地址间的映射
- 索引分布在超级节点上
课后实践
- 查阅Skype应用的相关资料,就其架构,协议,算法等撰写一篇调研报告,长度在5000字以上
七. Socket 编程
网络程序接口设计(基于端系统)
- Web/RPC/中间件编程
- 基于Web的开发,典型CS架构应用
- Web编程,实质上是基于Web应用基础上的二次开发
- 即基于应用层之上的开发
- Socket编程(重点)
- NetBIOS编程(Windows)
- 基于OSI会话层的开发
- 基于C语言标准库函数编程
- LibPcap/WinPcap,针对于Linux,unix操作系统数据包的抓包功能
- Libnet,用于网络分组数据的构造和发送
- Libnids,面向网络入侵检测
- Libicpm,用于icmp协议的报文构造和发送处理的
- 可用于编写网络扫描工具,网络入侵检测,防火墙等应用
- 基于NDIS(网络驱动程序接口规范 (Network Driver Interface Specification))网络编程(Windows)
- 基于Packet Driver编程(面向数据链路层)
- 屏蔽网卡实现细节
- 适用于所以网卡
- 直接网卡编程
- 涉及硬件相关编程,难以掌握
- 小练习
应用编程接口API
- 作为网络应用程序,事实上是需要实现两个主机上运行的两个应用的通讯(通过互联网)
- 通讯遵顼通信协议
- 两个端系统从功能上可视为5个层次
- 此处的API主要指应用层和传输层之间的接口问题
- 应用编程接口API : 就是应用进程控制权和操作系统控制权进行转换的一个系统调用接口
- 应用进程通过API,将控制权交由操作系统,操作系统调用相应过程,完成之后将结果返回给应用进程
几种典型的应用编程接口
- Berkeley UNIX操作系统定义了一种API,称为套接字接口(socket interface),简称套接字(socket)
- Micorsoft 在其操作系统中采用套接字接口API,形成了一个稍有不同的API,称之为Windows Socket Interface,WINSOCK.
- AT&T为其UNIX系统V定义了一种API,简写为TLI(Transport Layer Interface).
Socket API
- 最初设计
- 面向BSD UNIX-Berkeley
- 套接字API最初由berkeley在一个国防项目中设计开发的接口
- 面向TCP/IP协议栈接口
- 该项目最初为了在Berkeley的Unix中实现TCP/IP的协议簇
- 事实上,现今套接字API可面向多种协议栈
- 面向BSD UNIX-Berkeley
- 目前
- 事实上的工业标准
- 绝大多数操作系统都支持
- Linux/Unix均基于标准Berkelry-socket
- Windows基于WinSocket
- Internet网络应用最经典的API接口
- 通信模型
- 客户/服务器(C/S)(最基础模型)
- 提供用于进程通信的抽象机制
- 标识通信端点(对外)
- IP地址 端口号
- 操作系统/进程管理套接字(对内)
- 套接字描述符 (socket descriptor)
- 小整数
- 小练习
- 套接字描述符 (socket descriptor)
Socket抽象
- 套接字描述符的本质是Socket抽象
- 类似于文件的抽象
- 当应用进程创建套接字时,
- 操作系统分配一个数据结构存储该套接字相关信息
- 返回套接字描述符号
- 每个进程对应有一张Socket描述符表,记录进程管理的Socket信息
- 每创建一个套接字,均会在表中增加一个指向新增套接字的指针
- Socket使用一个数据结构维护了其记录的信息
- 最重要的信息==>地址信息==>IP地址 端口号==端点地址
- 使用套接字进行通信时,需要指定套接字的本地以及远端的端点地址
- 需要使用地址结构进行指定
地址结构
- 已定义结构sockaddr_in:
struct sockaddr_in{
u_char sin_len; //地址长度
u_char sin_family; //地址族(TCP/IP : AF_INET)
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sin_zero[8]; //未用(置0)
}
- 使用TCP/IP协议簇的网络应用程序声明端点地址变量时,
- 使用结构sockaddr_in,声明端点地址变量,赋值
- 地址族(TCP/IP : AF_INET)
Socket API函数 (WinSock)
- WinSock对Berkeley-Socket进行扩展,所扩展出来的函数以WSA(Windows Socket API)-为前缀
初始调用==>WSAStartup()
代码语言:javascript复制int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);
- 使用Socket的应用程序在使用Socket
- 之前必须首先调用WSAStartup函数
- 最后必须调用WSACleanup函数(释放所使用的Windows SOcket DLL)
- 原因 : WSASocket的实现机制是以Windows操作系统的动态链接库方式实现
- 两个参数:
- 第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本,低位字节指明主版本
- 十六进制整数,例如0x102表示2.1版
- 第二个参数指明程序请求使用的WinSocket的版本信息
- 指向WSDATA结构指针
- 第一个参数指明程序请求使用的WinSock版本,其中高位字节指明副版本,低位字节指明主版本
wVersionRequested = MAKEWORD(2,1);
err = WSAStartup(wVersionRequested,&wsaData);
使用结束==>调用WSACleanup
代码语言:javascript复制int WSACleanup(void);
- 应用程序在完成对请求的Socket库的使用,最后要调用WSACleanup函数
- 解除与Socket库的绑定
- 释放Socket库所占用的系统资源
Socket API (Berkeley Socket)
- 除了WSAStartup和WSACleanup两个WSA为前缀的SocketAPI只能在Windows环境下使用外
- 其他没有以WSA开头的SocketAPI在WinSocket下可以使用,在Linux/Unix操作系统使用的Berkely-Socket也能使用
socket
代码语言:javascript复制sd = socket(protofamily,type,proto); //作用==>创建套接字
- 应用进程要通信就必须==>创建套接字
- 操作系统返回套接字描述符(sd)
- 第一个参数(协议簇) : protofamily = PF_INET (TCP/IP)
- 套接字API的抽象最初目的是面向TCP/IP,最终实现可面向多种类型协议
- 第二个参数(套接字类型)
type = SOCK_STREAM,SOCK_DGRAM or SOCK_RAW (TCP/IP)
- 第三个参数(协议号) 默认0
struct protoent *p;
p = getprotobyname("tcp");
SOCKET sd = soket(PF_INET,SOCK_STREAM,p->p_proto);
Socket面向TCP/IP的服务类型
代码语言:javascript复制sd = socket(protofamily,type,proto); //作用==>创建套接字
/*参数说明
protofamily : 套接字协议族,用于指明使用的协议PF_INET表示面向TCP/IP
type : 套接字类型
SOCK_STREAM , 流式套接字 面向TCP
SOCK_DGRAM , 数据报套接字 Data GRAM ,面向UDP
SOCK_RAW , 原始套接字 面向网络层IP/ICMP/IGMP...
操作系统对其套接字创建需要特殊权限(root/admin)
proto : protocol的缩写 ,指明协议号,默认0
*/
- 小练习
closesocket/close
代码语言:javascript复制在WinSocket中是closesocket(),在BerkeleySocket中是close()
int closesocket(SOCKET sd); //关闭一个描述符为sd的套接字
//返回值为0==>执行成功
//返回值为SOCK_ERROR==>执行失败
- 若有多个进程共享一个套接字时,调用closesocket/close会将套接字引用计数减1,直至0才关闭套接字
- 若一个进程中的多个线程对一个套接字无计数
- 也就是说,若进程中的一个线程调用了closesocket/close将一个套接字关闭,则该进程中的其它线程也将不能访问该套接字
bind
代码语言:javascript复制int bind(sd,localaddr,addrlen); //绑定套接字本地端点地址==>IP地址 端口号(16进制长度)
- 参数列表
- 套接字描述符 : sd
- 端点地址 : localaddr = IP PN
- 结构 : socketaddr_in
- 客户程序一般不必显式调用bind函数
- OS会自动设置客户端套接字的本地IP和PN
- 对于服务器端
- 熟知PN,80端口(http)
- 对于IP?
- 解决方案==>使用地址通配符: INADDR_ANY
- 作用:声明在此服务器运行的此主机中任何一个有效的IP地址均可以访问
listen
代码语言:javascript复制int listen(sd,queuesize); //将服务器端流套接字置于监听状态
//返回值为0==>执行成功
//返回值为SOCK_ERROR==>执行失败
- 仅服务器端调用,面向连接的流套接字
- 设置缓存连接请求队列大小(queuesize)
connect
代码语言:javascript复制connect(sd,saddr,saddrlen); //客户机套接字与目的主机套接字的端点地址进行连接
- 仅用于客户端
- 可用于TCP客户端也可用于UDP客户端
- TCP客户端 : 建立TCP连接
- UDP客户端 : 指定服务器端点地址
- 与TCP不同UDP面向无连接,即使请求成功也不一定能与服务端进行通信
- 作用
- 客户端调用connect函数使客户端套接字sd与特定计算机的特定端口saddr的套接字(服务)进行连接
accept
代码语言:javascript复制newsock = accpet(sd,caddr,caddrlen);
- 服务程序调用accept函数从处于监听状态的流套接字sd的客户端请求队列中取出排在最前面的一个客户请求,并且创建一个新的套接字用于与客户端套接字创建连接通道
- 仅用于服务器端基于TCP协议的流套接字
- 利用新创建的套接字(newsock)与客户通信
- 采用accpet创建连接通道newsock的机制是基于tcp并发服务需求
- 若不建立连接通道,由于点对点,每次CS连接主套接字永远在某一时刻只能为某一客户提供服务
send,sendto
代码语言:javascript复制//发送数据
send(sd,*buf,len,flags);
sendto(sd,*buf,len,flags,destaddr,addrlen);
- send函数TCP套接字(客户与服务器)或调用了connect函数的UDP客户端套接字
- sendto函数用于UDP服务器端套接字与未调用connect函数的UDP客户端套接字
recv,recvfrom
代码语言:javascript复制//接收数据
recv(sd,*buffer,len,flags);
recvfrom(sd,*buf,len,flags,senderaddr,saddrlen);
- recv函数从TCP连接的另一端接收数据,或从调用了connect函数的UDP客户端套接字接收服务器发来的数据
- recvform函数用于从UDP服务器端套接字与未调用connect函数的UDP客户端套接字接收对端数据
setsockopt,getsockopt
代码语言:javascript复制int setsockopt(int sd,int level,int optname,*optval,int optlen);
int getsockopt(int sd,int level,int optname,*optval,socklen_t,*optval)
- setsockopt()函数用于设置套接字sd的选项参数
- getsockopt()函数用于获取任意类型,任意状态套接口的选项当前值,并将结果存入optval
- 可用参数格式
- IP==>ipv6/ipv4,TCP参数等
小结
WinSock体系下基于Windows | WSAStartup | 初始化socket库 |
---|---|---|
WSACleanup | 清除/终止sicket库的使用 | |
BerkeleySock体系下基于Linux/Unix | socket | 创建套接字 |
connect(C端专用) | 发起请求"连接"远端服务器 | |
closesocket/close | 释放/关闭套接字(前者基于WinSock,后者基于BerkeleySock) | |
bind(通常C端由OS调用) | 绑定套接字本地IP和端口号 | |
listen | 设置TCP套接字为监听模式,同时设置请求队列大小 | |
accept | 接收/提取一个连接请求,创建新套接字(建立连接通道) | |
send | 发送数据(TCP套接字/连接模式的C端UDP套接字) | |
sendto | 发送数据报(非连接的UDP) | |
recv | 接收数据(TCP套接字/连接模式的C端UDP套接字) | |
recvfrom | 接收数据报(非连接的UDP) | |
setsockopt | 设置套接字选项参数 | |
getsockopt | 获取套接字选项参数 |
- 小练习
网络字节顺序(Network Byte Order)
- 在OSI7层模型中第六层表示层解决的问题是字节数据表示转换任务
- 但两个系统进行通信时,两端系统数据表述不同时,表示层要进行转换
- 在5层参考模型中没有表示层
- 在传输层中进行解决
- TCP/IP定义了标准的用于协议头中的二进制整数表示:网络字节顺序(network byte order)
- 某些Socket API函数的参数需要存储为网络字节顺序(IP地址,端口号等)
- NBO可以实现本地字节顺序与网络字节顺序间的转换函数
- htons : 本地字节顺序==>网络字节顺序(16bits)(host to network short)
- ntohs : 网络字节顺序==>本地字节顺序(16bits)
- htonl : 本地字节顺序==>网络字节顺序(32bits)
- ntohl : 网络字节顺序==>本地字节顺序(32bits)
- 根据Socket API函数所需的参数类型,进行转换
网络应用的Socket API(TCP)调用基本流程
客户端软件设计
解析服务器IP地址
当用户使用服务端域名/点分十进制标识IP地址时,需要将域名和IP地址进行转换
- 客户端可能使用域名(ex:study.163.com)或IP地(ex : 192.168.10.100)标识服务器
- IP协议需要使用32位二进制IP地址;需要将域名或IP地址转换为32位IP地址
- 函数inet_addr()实现点分十进制IP地址到32位IP地址转换
- 函数gethostbyname()实现域名到32位IP地址转换
- 返回一个指向hostent的结构指针
- 由上述两个函数解析获得的IP地址为网络字节顺序,可直接被套接字API进行传递/赋值
struct hostent{
char FAR* h_name; //official host name
char FAR*FAR* h_aliases; //other aliases
short h_addrtype; //address type
short h_lengty; //address length
char FAR*FAR* h_addr_list; //list of address
};
#define h_addr h_addr_list[0]
解析服务器(熟知)端口号
- 客户端软件在标识与目的主机进行通信时还需指定端口号
- 客户端还可能使用服务名(如HTTP)标识服务器端口(80)
- 函数getservbyname().返回一个指向servent的结构指针
struct servent{
char FAR* s_name; //official service name
char FAR*FAR* s_aliases; //other aliases
short s_port; //port for this service
char FAR* s_proti; //protocal to use
};
解析协议号
- 在整个协议中,所有协议使用协议号进行标识
- 客户端可能使用协议名(ex: TCP)指定协议
- 需要将协议名转换为协议号(ex :80)
- 函数getprotobyname(),实现协议名到协议号的转换,返回一个指向proroent的结构指针
struct protoent{
char FAR* p_name; //official protocol name
char FAR*FAR* p_aliases; //list of aliases allowed
short p_proto; //official protocol number
};
TCP客户端软件流程
UDP客户端软件流程
客户端软件实现
connectsock()
- 设计一个connectsock过程封装底层代码
UDP客户端
- 设计connectUDP过程用于创建连接模式客户端UDP套接字
TCP客户端
- 设计connectTCP过程,用于创建客户端TCP套接字
异常处理
案例 : 访问DAYTIME服务的客户端(TCP)
- DAYTIME服务
- 获取日期和时间
- 双协议服务(TCP,UDP),端口号13
- TCP版利用TCP连接请求触发服务
- 客户端无需发送任何信息,仅需发起一个TCP请求连接,连接成功服务器便主动发送时间日期
- UDP版需要客户端发送一个请求
- 客户端需要向服务器发送信息,内容无需关注,仅需让服务器知道是哪个客户端发的即可
案例 : 访问DAYTIME服务的客户端(UDP)
服务器软件设计
4种类型基本服务器
- 循环无连接(Iterative connectionless)服务器
- 基本流程
- 发送数据报
- 服务器段不能使用connect()函数,无连接服务器使用sendto()函数
retcode = sendto(socket,data,length,flags,destaddr,addrlen);
/*
参数列表
socket --服务器UDP套接字
data --存储待发送数据的缓存地址(指针类型)
length --缓存中数据字节数
flags --调试与控制选项的标志位(常置0)
destaddr --指向sockaddr_in指针(客户端端点地址)
addrlen --地址结构长度
*/
- 获取客户端地址
- 调用recvfrom()函数接收数据时,自动提取
retcode = recvfrom(socket,buf,length,flags,from,fromlen);
/*
参数列表
socket --服务器UDP套接字
buf --存储数据报缓存地址(指针类型)
length --缓存中数据字节数
flags --调试与控制选项的标志位(常置0)
from --指向sockaddr_in缓存地址(客户端端点地址)
fromlen --源地址结构长度
*/
- 循环面向连接(Iterative c onnection-oriented)服务器
- 基本流程
- 并发无连接(Concurrent connectionless)服务器
- 基本流程
- 并发面向连接(Concurrent connection-oriented)服务器
- 基本流程
服务器软件实现
- 设计一个底层过程隐藏底层代码
- passivesock()
- 两个高层过程分别用于创建服务器端UDP套接字和TCP套接字(调用passivesock()函数)
- passiveUDP()
- passiveTCP()