计算机网络第二章-应用层(下)

🎈DNS

DNS(Domain Name System)的必要性

  • IP地址标识主机,路由器
  • 但IP地址不好记忆,不便人类使用(没有意义)
  • 人类一般倾向于使用一些有意义的字符串来标识Internet上的设备
  • 存在着"字符串"-IP地址的转换的必要性
  • 人类用户提供要访问机器的"字符串"名称
  • 由DNS负责转换为二进制的网络地址

DNS的历史

  • ARPANET的名字解析解决方案
    1.主机名:没有层次的一个字符串(一个平面)
    2.存在着一个(集中)维护站:维护着一张主机名-IP地址的映射文件:Hosts.txt
    3.每台主机定时从维护站取文件
  • ARPANET解决方案的问题
    1.当网络中主机数量很大时
    没有层次的主机名称很难分配
    文件的管理,发布,查找都很麻烦

DNS总体思路和目标

DNS的主要思路

  • 分层的,基于域的命名机制
  • 诺干分布式的数据库完成名字到IP地址的转换
  • 运行在UDP之上端口号为53的应用服务
  • 核心的Internet功能,但以应用层协议实现
    1.在网络边缘处理复杂性

DNS主要目的

  • 实现主机名-IP地址的转换(name/IP translate)
  • 其他目的
    1.主机别名规范名字的转换:Host aliasing
    2.邮件服务器别名到邮件服务器的正规名字的转换:Mail server aliasing
    3.负载均衡:Lood Distribution

问题1:DNS名字空间

DNS域名结构

  • 一个层面命名设备会由很多重名
  • NDS采用层次树状结构的命名方式
  • Internet根被为几百个顶级域(top lever domains)
    1.通用的(.com .edu .gov .int .mil .net .org .firm .hsop .web .arts .res)
    2.国家的(.cn .us .nl .jp)
  • 每个(子)域下面可划分为诺干子域
  • 树叶是主机

域名(Domain Name)

  • 从本域往上,直到树根
  • 中间使用"."间隔不用的级别
  • 例如:ustc.edu.cn
  • 域的域名:可以用于表示一个域
  • 主机的域名:一个域上的一个主机

域名的管理

  • 一个域管理其下的子域
    1.jp被划分为ac.jp co.jp
    2.cn被划分为edu.cn com.cn
  • 创建一个新的域,必须征得它所属域的同意

域与物理网络无关

  • 域遵从组织界限,而不是物理网络
    1.一个域的主机可以不在一个网络
    2.一个网络的主机不一定在一个域
  • 域的划分是逻辑的,而不是物理的

问题2:解析问题-名字服务器

一个名字服务器的问题

  • 可靠性问题:单点故障
  • 扩展性问题:通信容量
  • 维护问题: 远距离的集中式数据库

区域(zone)

  • 区域的划分有区域管理者自己决定
  • 将DNS名字空间划分为互不相交的区域,每个区域都是树的一部分
  • 名字服务器:
    1.每个区域都有一个名字服务器:维护着它所管辖区域的权威信息(authoritative record)
    2.名字服务器允许被放置在区域之外,以保障可靠性

区域名字服务器维护资源记录

  • 资源记录(resource records)
    1.作用:维护 域名-IP(其它)的映射关系
    2.位置:Name Server的分布式数据库中
  • RR格式:(domain_main ,ttl,type,class,value)
    1.Domain_name:域名
    2.Ttl:time to live:生存时间(权威记录(长期),缓冲记录(一般2天))
    3.Class类别:对于Internet,值为IN
    4.Value值:可以是数字,域名或ASCII串
    5.Type类别:资源记录的类型

DNS大致工作过程

  • 应用调用 解析器(resolver)
  • 解析器作为客户向Name Server发出查询报文(封装在UDP段中)
  • Name Server返回响应报文(name/ip)

本地名字服务器(Local Name Server)

  • 并不严格属于层次结构
  • 每个ISP(居民区的ISP,公司,大学)都有一个本地DNS服务器
    1.也称为"默认名字服务器"
  • 当一个主机发起一个DNS查询时,查询被送到其本地DNS服务器
    1.起着代理的作用,将查询转发到层次结构中

名字服务器(Name Server)

名字解析过程

  • 目标名字在Local Name Server中
    情况1:查询的名字在该区域内部
    情况2:缓存(cashing)

当与本地名字服务器不能解析名字时,联系根名字服务器顺着根-TLD一直找到权威名字服务器

递归查询

递归查询:

  • 名字解析负担都放在当前联络的名字服务器上

  • 问题:根服务器负担太重

  • 解决:迭代查询

迭代查询

  • 根(及各级域名)服务器返回的不是查询结果,而是下一个NS的地址
  • 最后由权威名字服务器给出解析结果
  • 当前联络的服务器给出可以联系的服务器名字
  • "我不知道这个名字,但可以向这个服务器请求"

DNS协议,报文

DNS协议:查询响应报文的报文格式相同

提高性能:缓存

  • 一旦名字服务器学到了一个映射,就将该映射缓存起来
  • 根服务器通常都在本地服务器中缓存着(使得根服务器不用经常被访问)
  • 目的:提高效率
  • 可能存在的问题:如果情况变化,缓存结果和权威资源不一致
  • 解决方案: TTL (默认2天)

问题3:维护问题:新增一个域

攻击DNS

DDoS攻击

  • 对根服务器进行流量轰炸攻击:发送大量ping
    1.没有成功
    2.原因1:根目录服务器配置了流量过滤器,防火墙
    3.原因2: Local DNS服务器缓存了TLD服务器的IP地址,因此无需查询根服务器
  • 向TLD服务器流量轰炸攻击:发送大量查询
    1.可能更危险
    2.效果一般,大部分DNS缓存了TLD

重定向攻击

  • 中间人攻击
    1.截获查询,伪造回答,从而攻击某个(DNS回答指定的IP)站点
  • DNS中毒
    1.发送伪造的应答给DNS服务器,希望它能够缓存这个虚假的结果
  • 技术上较困难:分布式截获和伪造利用DNS基础设施进行DDoS
  • 伪造某个IP进行查询, 攻击这个目标IP
  • 查询放大,响应报文比查询报文大.
  • 效果有限

🎈P2P应用

纯P2P架构

  • 没有(或极少)一直运行的服务器
  • 任意端系统都可以直接通信
  • 利用peer的服务能力
  • Peer节点间歇上网,每次IP地址都有可能变化

例子:
文件分发(BitTorrent)
流媒体(KanKan)
VoIP (Skype)

文件分发(c/s和P2P对比)

文件分发时间:C/S模式

  • 服务器传输:都是有服务器发送给peer,服务器必须顺序传输(上载)N个文件拷贝:
    1.发送一个copy:F/us
    2.发送N个copy:NF/us

  • 客户端:每个客户端必须下载一个文件拷贝
    1.dmin=客户端最小的下载速率
    2.下载带宽最小的客户端下载的时间:F/dmin

文件分发时间:P2P模式

  • 服务器传输:最少需要上载一份拷贝
    1.发送一个拷贝的时间:F/us

  • 客户端:每个客户端必须下载一个拷贝
    1.最小下载带宽客户单耗时:F/dmin

  • 客户端:所有客户端总体下载量NF
    1.最大上载带宽是:us+∑ui
    2.除了服务器可以上载,其他所有的peer节点都可以上载

对比

上课截图:

P2P:集中式目录

最初的"Napster"设计:当对等连接时,它告知中心服务器: IP地址和内容

P2P:集中式目录存在的问题(文件传输是分散的,而定位内容则是高度集中的)

  • 单点故障
  • 性能瓶颈
  • 侵犯版权

P2P:完全分布式

查询洪泛:Gnutella

  • 全分布式
    1.没有中心服务器
  • 开放文件共享协议
  • 许多Gnutella客户端实现了Gnutella协议
    1.类似HTTP有许多浏览器

覆盖网络:图

  • 如果X和Y之间有一个TCP连接,则二者之间存在一条边
  • 所有活动的对等方和边就是覆盖网络
  • 边并不是物理链路
  • 给定一个对等方,通常所连接的节点少于10个

Gnutella:协议

  • 在已有的TCP连接上发送查询报文
  • 对等方转发查询报文
  • 以反方向返回查询命中报文

可扩展性:限制范围的洪泛查询

Gnutella:对等方加入

  • 1.对等方X必须首先发现某些已经在覆盖网络中的其他对等方:使用可用对等方列表
    自己维持一张对等方列表(经常开机的对等方的IP)联系维持列表的Gnutella站点
  • 2.X接着试图与该列表上的对等方建立TCP连接,直到与某个对等方Y建立连接
  • 3.X向Y发送一个Ping报文,Y转发该Ping报文
  • 4.所有收 到Ping报文的对等方以Pong报文响应IP地址、共享文件的数量及总字节数
  • 5.X收到许 多Pong报文,然后它能建立其他TCP连接

P2P:混合体

利用不匀称性: KaZaA

  • 每个对等方要么是一个组长,要么隶属于一个组长
    1.对等方与其组长之间有TCP连接
    2.组长对之间有TCP连接
  • 组长跟踪其所有的孩子的内容
  • 组长与其他组长联系
    1.转发查询到其他组长
    2.获得其他组长的数据拷贝

KaZaA:查询

  • 每个文件有一个散列标识码和一个描述符
  • 客户端向其组长发送关键字查询
  • 组长用匹配进行响应:
    对每个匹配:元数据、散列标识码和IP地址
  • 如果组长将查询转发给其他组长,其他组长也以匹配进行响应
  • 客户端选择要下载的文件
    向拥有文件的对等方发送一个带散列标识码的HTTP请求

KaZaA小技巧

  • 请求排队
    限制并行上载的数量
    确保每个被传输的文件从上载节点接收一定量的带宽
  • 激励优先权
    鼓励用户上载文件
    加强系统的扩展性
  • 并行下载
    从多个对等方下载同一个文件的不同部分(HTTP的字节范围首部,更快地检索一个文件)

Distributed Hash Table(DHT)

  • 哈希表
  • DHT方案
  • 环形DHT以及覆盖网络
  • Peer波动

P2P文件分发:BitTorrent

  • 文件被分为一个个块256KB
  • 网络中的这邪恶peers发送接收文件块,互相服务

tracker:跟踪torrent中参与节点
Torrent(洪流):节点的组,之间交换文件快

  • Peer加入torrent:
    一开始没有块,但是将会通过其他节点处累积文件块
    向跟踪服务器注册,获得peer节点列表,和部分peer节点构成邻居关系(“连接")

  • 当peer下载时,该peer 可以同时向其他节点提供上载服务

  • Peer可能会变换用于交换块的peer节点

  • 扰动churn: peer节点可能会上线或者下线

  • 一旦一个peer拥有整个文件,它会(自私的)离开或者保留(利他主义)在torrent中

BitTorrent:请求,发送文件块

请求块:

  • 在任何给定时间,不同peer节点拥有一个文件块的子集
  • 周期性的,A节点向邻居询问他们拥有哪些块的信息
  • A向peer节点请求它希望的块,稀缺的块

发送块:一报还一报tit-for-tat

  • A向4个peer发送块,这些块向它自己提供最大带宽的服务
    其他peer被A阻塞(将不会从A处获得服务)
    每10秒重新评估一次:前4位
  • 每个30秒:随机选择其他peer节点,向这个节点块
    "优化疏通"这个节点
    新选择的节点可以加入这个top4

更高的上载速率:发现更好的交易伙伴,获得更快的文件传输速率

P2P文件共享

两大问题:如何定位所需资源,如何处理对等方的加入和离开

可能的方案:集中,分散,半分散

🎈CDN(Content Delivery Network)

CDN网络中包含的功能实体包括内容缓存设备、内容交换机、内容路由器、CDN内容管理系统等组成

视频流化服务和CDN:上下文

CDN主要功能

  • (1)节省骨干网带宽,减少带宽需求量;
  • (2)提供服务器端加速,解决由于用户访问量大造成的服务器过载问题;
  • (3)服务商能使用Web Cache技术在本地缓存用户访问过的Web页面和对象,实现相同对象的访问无须占用主干的出口带宽,并提高用户访问因特网页面的相应时间的需求;
  • (4)能克服网站分布不均的问题,并且能降低网站自身建设和维护成本;
  • (5)降低“通信风暴”的影响,提高网络访问的稳定性;

CDN基本原理

是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求

多媒体流化服务:DASH(Dynamic Adaptive Streaming over HTTP)

“智能”客户端:客户端自适应决定

  • 什么时候去请求块(不至于缓存挨饿,或者溢出)
  • 请求什么编码速率的视频块(当带宽够用时,请求高质量的视频块)
  • 哪里去请求块(可以向离自己近的服务器发送URL,或者向高可用带宽的服务器请求)

服务模式

  • 内容分发网络(CDN)是一种新型网络构建方式,它是为能在传统的IP网发布宽带丰富媒体而特别优化的网络覆盖层;而从广义的角度,CDN代表了一种基于质量与秩序的网络服务模式。

  • 内容分发网络(CDN)是一个经策略性部署的整体系统,包括分布式存储、负载均衡、网络请求的重定向和内容管理4个要件,而内容管理和全局的网络流量管理(Traffic Management)是CDN的核心所在。通过用户就近性和服务器负载的判断,CDN确保内容以一种极为高效的方式为用户的请求提供服务。

  • 内容服务基于缓存服务器,也称作代理缓存(Surrogate),它位于网络的边缘,距用户仅有"一跳"(Single Hop)之遥。同时,代理缓存是内容提供商源服务器(通常位于CDN服务提供商的数据中心)的一个透明镜像。这样的架构使得CDN服务提供商能够代表他们客户,即内容供应商,向最终用户提供尽可能好的体验,而这些用户是不能容忍请求响应时间有任何延迟的。

这个CDN学的很糟糕,感觉听懂了,又感觉没听懂

🎈TCP套接字编程

应用进程使用传输层提供的服务才能够交换报文,实现应用协议,实现应用

TCP/IP:应用进程使用socket API访问传输服务

地点:界面上的SAP(socket) 方式:Socket API

目标:学习如何构建能借助sockets进行通信的C/S应用程序

socket:分布式应用进程之间的门,传输层协议提供的端到端服务接口

TCP服务:从一个进程向另一个进程可靠地传输字节流

TCP套接字编程

服务器首先运行,等待连接建立

  1. 服务器进程必须先处于运行状态
  • 创建欢迎socket
  • 和本地端口捆绑
  • 在欢迎socket上阻塞式等待接收用户的连接

客户端主动和服务器建立连接

  1. 创建客户端本地套接字(隐式捆绑到本地port)
  • 指定服务器进程的IP地址和端口号,与服务器进程连接

3.当与客户端连接请求到来时

  • 服务器接受来自用户端的请求,解除阻塞式等待,返回一个新的socket(与欢迎socket不一样),与客户端通信
    允许服务器与多个客户端通信
    使用源IP和源端口来区分不同的客户端

数据结构sockaddr_in

IP地址和port捆绑关系的数据结构(标示进程的端节点)

struct sockaddr_in{
    short sin_family;//AF_INET
    unsigned short sin_port; //port
    struct in_addr sin_addr; //IP address,unsigned long
     
    char sin_zero[8]; //align
}

数据结构hostent

域名和IP地址的数据结构

struct hostent{
    char *h_name;
    char **h_aliases;
    int h_addrtype;
    int h_length; //地址长度
    char **h_addr_list;
    #define h_addr h_addr_list[0];
};

作为调用域名解析函数时的参数返回后,将IP地址copy到sockaddr_in的IP地址部分

例子:C客户端(TCP)

void main(int argc,char *argv[]){//client
    struct sockaddr_in sad;
    int clientSocket;
    struct hostent *ptrh;
    
    char Sentence[128];
    char modifiedSentence[128];

    int host=argv[1];
    int port=atoi(argv[2]);//atoi字符串转换数字

    clientSocket=socket(PF_INET,SOCK_STREAM,0);
    memset((char *)&sad,0,sizeof(sad));//clear sockaddr stricture
    sad.sin_family=AF_INET;//set family to Internet
    sad.sin_port=htons((unsigned short)port);
    ptrh=gethostbyname(host);//convert host name to IP address
    memcpy(&sad.sin_addr,ptrh->h_addr,ptrh->h_length);//将IP地址拷贝到sad.sin_addr
    connect(clientSocket,(struct sockaddr *)&sad,sizeof(sad));

    gets(Sentence);//Get input stream from user

    n=write(clientSocket,Sentence,strlen(Sentence)+1);//send line to server

    n=read(clientSocket,modifiedSentence,sizeof(modifiedSentence));//Read line from server
    printf("FROM SERVER:%s\n",modifiedSentence);

    close(clientSocket);//close connection
}

另外的视频我写了尝试了代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define SERV_PORT 9999

void sys_err(const char *str){
    perror(str);
    exit(1);
}

int main(int argc,char *argv[]){

        int cfd;
        char buf[BUFSIZ];
        int number=9;

        struct sockaddr_in serv_addr;
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_port= htons(SERV_PORT);
        
        inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr.s_addr);
        
        cfd = socket(AF_INET,SOCK_STREAM,0);

        if(cfd==-1){
            sys_err("socket error");
        }
        
        int ret = connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

        if(ret==-1){
            sys_err("connect error");
        }

        while(number--){
            write(cfd,"hello",5);
            ret=read(cfd,buf,sizeof(buf));
            write(STDOUT_FILENO,buf,ret);
        }
        close(cfd);

    return 0;
}

例子:C服务器(TCP)

void main(int argc,char *argv[]){//server
    struct sockaddr_in sad;
    struct sockaddr_in cad;
    int welcomeSocket,connectionSocket;
    struct hostent *ptrh;
    
    char clientSentence[128];
    char capitalizedSentence[128];

    int port=atoi(argv[1]);

    welcomeSocket=socket(PF_INET,SOCK_STREAM,0);

    memset((char *)&sad,0,sizeof(sad));

    sad.sin_family=AF_INET;
    sad.sin_addr.s_addr=INADDR_ANY;
    sad.sin_port=htons((unsigned short)port);

    bind(welcomeSocket,(struct sockaddr *)&sad,sizeof(sad));

    listen(welcomeSocket,10)

    while(1){
        
        connectionSocket=accept(welcomeSocket,(struct sockaddr *)&cad,&alen);

        n=read(connectionSocket,clientSentence,sizeof(clientSentence));

        n=write(connectionSocket,capitalizedSentence,strlen(capitalizedSentence)+1);

        close(connectionSocket);

    }
}

另外的视频我写了尝试了代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>

#define SERV_PORT 9999//这个335什么的不行,端口号有些不能用

void sys_err(const char *str){
    perror(str);
    exit(1);
}

int main(int argc,char *argv[]){
    
    struct sockaddr_in serv_addr,clit_addr;
    socklen_t clit_addr_len;

    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(SERV_PORT);
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

    int lfd=0, cfd=0,ret;
    char buf[BUFSIZ];

    lfd=socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1){
            sys_err("socket error");
    }

    bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));

    listen(lfd,128);

    clit_addr_len=sizeof(clit_addr);

    

    cfd=accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);

    if(cfd==-1){
        sys_err("accept error");
    }


    while(1){
        ret = read(cfd,buf,sizeof(buf));

        write(STDOUT_FILENO,buf,ret);

         for(int i=0;i<ret;i++){
                buf[i]=toupper(buf[i]);
         }
  
    write(cfd,buf,ret);
    }


    close(lfd);
    close(cfd);

    return 0;
}

其实还有一个UDP套接字但是感觉这个没有上机属实抽象,等我后面学TCP/IP这本书,顺便实践,最近也要备赛T^T,至少不要打铁

posted @ 2021-05-08 21:09  ouluy  阅读(428)  评论(0编辑  收藏  举报