zoukankan      html  css  js  c++  java
  • Python网络编程(基础总结、 入门经典)

    Linux下文件类型:
        bcd -lsp
             b(块、设备文件)
             c(字符设备文件)
             d(目录)
             -(普通文件)
             l(链接文件)
             s(套接字文件)
             p(管道文件)
        kill -sig pid:通过pid发送信号杀死指定进程
        kill -l:查看操作系统内所所有sig信号
        ps -aux ---> STAT表示进程状态
        信号:
            SIGHUP   断开链接
        SIGINT   Ctrl + c
        SIGQUIT  Ctrl +
        SIGTSTP  Ctrl + z
        SIGKILL  终止进程且不能被处理
        SIGSTOP  暂停进程且不能被处理
        SIGALRM  时钟信号
        SIGCHLD  子进程改变状态时父进程会收到此信号
     
     
    OSI七层模型 -----> 网络通信的标准化流程
     
        应用层: 提供用户服务,具体的内容由特定的程序规定
        表示层: 提供数据的加密和压缩优化
        会话层: 确定建立应用链接,选择传输服务
        传输层: 提供数据传输服务,进行流量控制
        网络层: 路由选着,网络互联
        链路层: 提供链路交换,具体消息的发送
        物理层: 物理硬件,接口,网卡的规定
    网络协议:
    应用层:TFTP(文件传输)、HTTP(超文本传输协议)、DNS(域名解析)、SMTP(邮件传输)
    传输层:TCP、UDP
    网络层:IP
    物理层:IEEE
    IP地址
    本地使用:127.0.0.1 或 “localhost”
    网络地址:“0.0.0.0” 或 “172.168.40.53”
    IPv4: 点分十进制   例如:192.168.1.3   取值0~255(32位)
    IPv6: 128位
     
     
    socket模块:
        ifconfig:查看本机IP (ens33:本地IP  lo:本地回还)
        ipconfig:windoes中
        socket.gethostname() :                                       获取本机主机名
        socket.gethostbyname('tedu') :                           利用主机名获取ip
        socket.gethostbyname('localhost'):                     获取本地ip
        socket.gethostbyaddr('127.0.0.1')                         访问主机IP地址
        socket.inet_aton('192.168.1.2')                             IP十六进制转换
        socket.inet_ntoa(b'xc0xa8x012')                     IP十进制转换
        socket.getservbyname('ssh')                                 获取应用程序的端口
    创建TCP服务端套接字:
    sockfd.socket(sock_family = AF_INET,
                   sock_tpye = SOCK_STREAM,
                   proto = 0)
        sockfd.bind(addr)                                              绑定地址
        sockfd.listen(n)                                                      设置监听套接字
      connfd,addr = sockfd.accept()                         等待接受客户端链接
      data = connfd.recv(buffersize)                           接收消息
      connfd.send(data)                                              发送消息
      sockfd.close()                                                     关闭套接字
    创建TCP客户端套接字:
    sockfd.socket(sock_family = AF_INET,
                   sock_tpye = SOCK_STREAM,
                   proto = 0)
        sockfd.bind(addr)                                             绑定地址
      sockfd.connect(addr)                                         链接服务端
      data = connfd.recv(buffersize)                           接收消息
      connfd.send(data) 发送消息
      sockfd.close() 关闭套接字
     
     
    创建UDP客户端套接字:
      sockfd.socket(sock_family = AF_INET,
                   sock_tpye = SOCK_DGRAM,
                   proto = 0)
        sockfd.bind(addr)                                            绑定地址
      data = sockfd.recvfrom(buffersize)                   接收消息
      sockfd.sendto(data, addr)                                发送消息
      sockfd.close()                                                   关闭套接字
    创建UDP客户端套接字:
      sockfd.socket(sock_family = AF_INET,
                   sock_tpye = SOCK_DGRAM,
                   proto = 0)
      data = sockfd.recvfrom(buffersize)                   接收消息
      sockfd.sendto(data, addr)                                发送消息
      sockfd.close()                                                  关闭套接字
     
    创建本地套接字服务端:
      sockfd = socket(AF_UNIX, SOCK_STREAM) 
      sockfd.bind(file)                                                绑定套接字文件
      sockfd.listen(3)                                                      监听
      connfd,addr = sockfd.accept()                               等待链接
      connfd.recv(buffersize)                                          接收消息
      connfd.send(data)                                                 发送消息
      sockfd.close()                                                        关闭套接字
    创建本地套接字客户端:
      sockfd = socket(AF_UNIX, SOCK_STREAM) 
      sockfd.connect(sock_file)                                     链接服务端
      connfd.recv(buffersize)                                         接收消息
      connfd.send(data)                                                 发送消息
      sockfd.close()                                                        关闭套接字
     
     
     
    套接字属性:
      sockfd.type                                                              返回套接字类型
      sockfd.family                                                           返回地址类型
    套接字方法:
      sockfd.fileno()                                                     获取套接字的文件描述符
      sockfd.getsockname()                                         获取套结字绑定的地址
      sockfd.getpeername()                                        获取链接套接字客户端的地址
      sockfd.setsockopt(level,optname, value)      设置端口可立即重用
      sockfd.setblocking(False)                                       将套接字设置为非阻塞状态
      sockfd.settimeout(sec)                                       设置套接字的超时时间
     
    select模块:                                                            IO多路复用,阻塞等待监控的IO事件发生
    rs, ws, xs = select(rlist,                                   等待处理的IO
                                         wlist                                     想要主动处理的IO
                                         xlist[,                                 出错希望去处理的IO
                                         timeout])                            超时检测
    p = select.poll                                                          创建poll对象
    p.register(s, POLLIN | PLLERR)                           注册关注的IO
    events = p.poll()                                                   监控关注的IO事件
     
    multiprocessing模块:                                            创建进程对象
    Process(target,                                                       要绑定的函数
                   name,                                                        给进程起的名称
                   args,                                                         元组给target函数位置传参
                   kwargs)                                                       字典给target函数键值传参
    p.start()                                                                     启动进程terget绑定函数
    p.join([timeout])                                                         阻塞等待子进程退出
    p.name                                                                       获取进程名(属性)
    p.daemon                                                                  设置为True主进程结束杀死所有子进程(必须start()前设置)
    p.is_alive()                                                                 判断进程是处于alive状态(存活)
    p.pid                                                                          获取创建进程的pid号(属性)
     
    pool = pool(x)                                                       创建进程池对象(进程池大小)
    pool.apply_async(fun,                                               要执行的函数(异步执行)
                                 args,                                             以元组形式为fun传参
                                 kwds)                                            以字典形式为fun传参
    pool.apply(fun, args, kwds)                                       (同步执行)
    r = pool.map(fun,range(6))                                         将要执行的事件放入进程池
    pool.close()                                                                关闭进程池
    pool.join()                                                                    回收进程池
     
    fd1,fd2 = Pipe(duplex=True)                                    创建管道(Flase:fd1只读,fd2只写)
    fd.recv()                                                                  从管道读取信息空则阻塞
    fd.send(data)                                                          向管道写入内容满则阻塞
     
    q = Queue(maxsize=0)                                            创建队列对象(存放多少条消息)
    q.put(data,                                                                   存入消息(支持Python数据类型)
               [block,                                                                默认阻塞 False:非阻塞
               timeout])                                                          block为True是表示超时检测
    data = q.get([block,timeout])                                         取出消息
    q.full()                                                                       判断队列是否为满
    q.empty()                                                                      判断队列是否为空
    q.qsize()                                                                       获取队列中消息的数量
    q.close()                                                                        关闭队列
     
    shm = Value(ctype,                                                  创建共享内存共享空间
                            obj)                                                       ctype字符串:(C语言数据类型),obj初始数据
    shm.value                                                                       表示共享内存的值(可以赋值)
    shm = Array(ctype,obj)                                          创建共享内存共享空间
     
    sem = Semaphore(num)                                          创建信号量
    sem.acquire()                                                            将信号量减1  0时阻塞
    sem.release()                                                            将信号量加1
    sem.get_value()                                                         获取当前信号量的值(数量)
     
    e = Event()                                                                创建Event事件对象
    e.wait([timeout])                                                         阻塞进程 直到事件对象被set
    e.set.()                                                                            让事件对象变为被设置状态
    e.clear()                                                                     使事件对象清除设置状态
    e.is_set()                                                                    判断当前事件是否被set
     
    Lock = Lock()                                                            创建锁对象
    lock.acquire()                                                            上锁
    lock.release()                                                                 解锁
     
    threading 模块:                                                            创建线程对象
    threshold.Thread(target,                                            线程函数
                                   name,                                                线程名字
                                    args,                                                元组给线程函数位置传参
                                    kwargs)                                         字典给线程函数位置传参
    t.start()                                                                       启动线程
    t.join()                                                                         回收线程
    t.name                                                                              线程名
    t.daemon = True                                                            主线程退出分支线程也退出
    t.setDaemon(True)                                                       主线程退出分支线程也退出
    t.isDaemon                                                                    查看daemon值
    t.setName(“name”)                                                   设置线程名称
    t.is_alive()                                                                 查看线程状态
    threading.currentThread()                                              获取当前进程对象
     
    e = threading.Event()                                                    创建Event事件对象
    e.wait([timeout])                                                             事件阻塞
    e.set()                                                                       设置事件
    e.clear()                                                                    清除事件
     
    lock = threading.Lock()                                            创建锁对象
    lock.acquire() 上锁
    lock.release() 解锁
     
     
    socketserver集成并发模块:
    StreamRequestHandler                                                处理tcp请求
    DatagramRequestHandler                                            处理udp请求
     
    ForkingMixIn                                                                  创建多进程
    ThreadingMixIn                                                             创建多线程
     
    TCPServer                                                                   创建tcp  server
    UDPServer                                                                  创建udp  server
     
    ForkingTCPServer                                                        ForkingMixIn  +  TCPServer 
    ForkingUDPServer                                                      ForkingMixIn  +  UDPServer 
    ThreadingTCPServer                                                    ThreadingMixIn  +  TCPServer 
    ThreadingUDPServer                                                    ThreadingMixIn  +  UDPServer 
     
     
     
    signal模块:
    signal.alarm(sec)                                                       设置时钟信号给自己SIGALRM信号
    signal.pause()                                                           阻塞进程,等待一个信号
    signal.signal(sig,                                                      要处理的信号
                            handler)                                               处理方法(SIG_DFL:默认 SIG_IGN:忽略 func:自定义函数)
     
     
    sys模块补充:
        sys.argv                              获取从命令行获取的参数内容列表
        sys.stdin      0                     标准输入IO文件描述符
        sys.stdout   1                      标准输出IO文件描述符
        sys.stderr   2                      错误IO文件描述符
        sys.exit([status])            退出一个进程(状态:退出提示字符串)
    字符串方法补充:
        S.splitlines                          按行分隔
     
    os模块补充:
    os.path.exists(file)                                 判断一个文件是否存在
    os.remove(file)                                      删除文件
    os.unlink(file)                                         删除文件
    pid = os.fork()                                             创建进程 失败-1 成功0
    os.getpid()                                             获取进程的PID号
    os.getppid()                                           获取父进程的PID
    os.exit(status)                                      退出一个进程(状态:整数 默认0)
    pid,status = os.wait()                                 塞等待处理子进程的退出
    os.WEXITSTATUS(status)                       获取原来退出状态
    pid,status = os.waitpid(pid,option)            阻塞等待处理子进程的退出
    os.path.getsize('./1.txt')                            读取文件的大小
    os.kill(pid,sig)                                    发送一个信号给某个进程
    os.listdir(path)                                         获取指定目录文件列表
    os.path.isfile()                                         判断一个 文件是否为普通文件
    os.path.isdir()                                         判断一个文件是否为目录 
     
     
    传输层服务:
        面向连接的传输服务(tcp协议):
            传输特征:
              可靠的数据传输:
                可靠性:无失序、无差错、无重复、无丢失、无重复
            在数据传输前和传输后需要建立连接和断开链接
              面向传输服务建立连接的过程:‘三次握手’
                1.客户端向服务器发送链接请求
                2.服务器接受到请求进行确认,返回确认报文
                3.客户端收到服务器回复最终确认链接
              面向传输服务断开链接的过程:‘四次挥手’
                1.主动方发送报文,告知被动方要断开链接
        2.被动方回复报文,表示已经接受到请求,准备断开
        3.被动方再次发送报文,表示准备处理就绪,可以断开
        4.主动方发送确认报文,断开链接
            应用情况:
        适用于传输较大的内容或文件,网络良好,
        需要保证传输可靠性的情况
        e.g.  信息聊天,文件上传下载,邮件,网页获取
     
        面向无连接的传输服务(udp协议):
            不保证传输的可靠性
    没有建立连接和断开的过程
    数据的收发比较自由
    适用情况: 
        网络情况较差,对可靠性要求不高,收发消息的两端
        e.g.:网络视频,群聊,广播等
     
     
    收发函数特性:
         recv特征:
           如果建立的另一端链接被断开, 则recv立即返回空字符串
           recv是从接受缓冲区取出内容,当缓冲区为空则阻塞
           recv如果一次接受不完缓冲区的内容,下次执行会自动接受
         send特征:
            如果发送的另一端不存在则会产生pipe...异常
    send是从发送缓冲区发送内容当缓冲区为满则堵塞
     
     
    http协议:
       超文本传输协议
       用途:
          网站中浏览区器网页的获取,基于网站事物数据传输
          编写基于http协议的数据传输
       特点:
          1.应用层协议,传输层使用tcp服务
          2.简单、灵活,可以使用多种编程语言操作
          3.无状态的协议,既不用记录用户的输入内容
          4.http1.1  ---> http2.0(还没发布)  技术的成熟和稳定性
       http请求(request):
          1.请求格式:
             1)请求行:说明具体的请求类别和内容
              GET    /index.html    /HTTP/1.1
                    请求类别   请求内容    协议版本
         2)请求类别:
            GET:获取网络资源
    POST:提交一定的附加数据
    HEAD:获取相应头
    PUT:更新服务器资源
    DELETE:删除服务器资源
    CONNECT:未使用
    TRACE:用于测试
    OPTIONS:获取服务器性能信息
     2.请求头:对请求的具体描述
         Accept:text/html
            每一个键值对占一行,描述了一个特定信息
     3.空行
     4.请求体:具体的参数或提交的内容
              get参数或者post提交的内容
       http响应(response):
          1.响应格式:
            1)响应行:反馈具体的响应情况
              HTTP/1.1     20       OK
      版本协议   响应码   附加信息
        3)响应码:
           1xx:提示信息,表示请求已经接收
           2xx:响应成功
           3xx:响应需要定向
           4xx:客户端错误
           5xx:服务器端错误
     3)常见响应码:
       200  成功
       404  请求内容不存在
       401  没有访问权限
       500  服务器未知错误
           503  服务器暂时无法执行
          2.响应头:对响应内容的具体描述
          3.空行
          4.响应体:
             将客户端请求内容进行返回
     
     
    IO多路复用
    定义:
    通过一个监测,可以同时监控多个IO事件的行为,
    当那个IO可以执行,让这个IO事件发生
    同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件
    此时形成多个IO时间都可以操作的现象,不必逐个等待执行
    IO准备就绪:
    IO事件即将发生时的临界状态是不可逆转的
    在程序中存在的IO事件中选择要监测的事件
    创建监测,将监测的IO事件注册
    等待监测的IO事件发生,判断是什么事件
    处理相应的IO
     
    poll方法实现IO多路复用:
       1.创建poll对象:
           p = select.poll
       2.注册关注的IO:
           p.register(s, POLLIN | PLLERR)
           不关注:
              p.unregister(s)
           事件类别:
              POLLIN  POLLOUT  POLLERR  POLLHUP   POLLPRI
              rlist    wlist    xlist     断开   紧急处理 
       3.监控IO:
           events = p.poll()
           功能:监控关注的IO事件
           返回值:
               返回发生IO事件
       events是一个列表[(fileno, evnet), (), ()....]
       每个就绪IO对应一个元组(描述符,就绪事件)
           IO地图:{s.fileno():s}
       4.处理IO事件
     
    位运算:
       按照二进制位进行位运算操作
       & 按为与   |按位或   ^按位异或
     
       << 左异    >>右移
     
       11  1011
       14  1110
     
       &   1010  有0得0
       |   1111  有1得1
       ^   0101  相同为0不同为1
      
       11 << 2  == 44   右侧补零(乘2乘2次)
       14 >> 2  == 3    挤掉右侧的数字(地板除2除2次)
    使用:
        1.在低层硬件时操作寄存器
        2.做标志位的过滤
     
     
     
    多任务编程:
        意义:
    充分利用计算机资源,同时运行多个任务,
    提高程序整体的运行效率
        定义:
    通过程序利用计算机的多个核心达到同时执行多个任务的目的
    因此达到提升程序运行效率的目的
        实施方案:
    多进程编程
    多线程编程
        并行:
            多个计算机核心在同时处理多个任务,
    这时多个任务之间是并行关系
         并发:
            同时运行多个任务,内核在多个任务之间的不断切换,
    达到多个任务都会执行的处理效果
    此时多个任务之间的并发关系
     
    程序:
        是一个可执行文件,是静态的,只占有磁盘
        不占用计算机运行资源
    进程:
        程序在计算机中的一次执行过程
        是一个动态过程,占有一定的计算机资源
        有一定的生命周期
    注:
        同一个程序不同的运行过程是不同的进程,
        因为分配的资源和生命周期都不同
     
    进程的创建过程:
        1.用户启动一个程序,或是调用接口发起进程创建
        2.操作系统接收用户请求分配计算机资源创建进程
        3.操作系统将一定状态的进程提供给用户使用
        4.用户利用操作提供的进程完成任务
     
    CPU时间片:
        如果有个进程占有CPU此时我们称为该进程占有CPU的时间片
        多个进程任务或轮流占有CPU时间片并形成并发效果
      
    进程信息(process)
        PCB(进程控制块):
             进程创建后 会自动在内存中产生一个空间存放进程信息
        进程信息:
            进程ID 进程占有内存的位置  创建时间、创建位置
    查看系统进程信息:ps -aux
        PID(process ID):
          在操作系统中每个进程都有唯一的PID值是由系统分配的
    进程特征:
        进程是操作系统分配资源的最小单元
        每个进程拥有自己独立的运行空间(4个G的虚拟内存空间)
        进程之间相互独立各不影响
    进程的状态:
        三态:
        就绪状态:
             进程具备执行条件,等待系统分配处理器资源进入运行态
        运行态:
             进程占有CPU处于运行状态
        等待态:
             进程暂时不具备运行条件,需要阻塞等待
     
        五态:
             在三态的基础上增加新建和终止态 
     新建:
       创建一个新的程序,获取系统资源的过程
     终止:
       进程执行结束,释放资源的过程
     
    ps -aux ---> STAT表示进程状态:
        D  等待态  阻塞  不可中断等待态
        S  等待态  睡眠  可中断等待态
        T  等待态  暂停  暂停执行
        R  运行态(就绪态)
        Z  僵尸
          +  前台进程(在终端运行)
          <  有较高优先级的进程
          N  较低优先级的进程
          s  回话组
          l  有进程链接
    进程的优先级:
        top 查看进程运行态优先级
        取值范围:-20~19   -20最高
        nice:
           以指定的优先级运行一个程序
           nice -9 ./hello.py  以9的优先级运行
           sudo nice --9 ./hello.py 以-9优先级运行
     
        首行添加 #! /usr/bin/python3  指定执行器
          执行:./hello.py
        修改程序权限添加可执行权限
             chmod 775 hello.py
     
    孤儿进程 : 
    当父进程先于子进程退出,此时子进程就会成为孤儿进程。
        * 孤儿进程会被系统指定进程收养,即系统进程会成为孤儿
          进程新的父进程。系统进程会自动处理孤儿进程退出状态
     
    僵尸进程 : 
    子进程先于父进程退出,父进程没有处理子进程的退出状态,此时子进程就会成为僵尸进程
    * 僵尸进程会滞留部分PCB信息在内存中,大量的僵尸进
       程会消耗系统的内存资源,所以要尽量避免僵尸进程产生
     
    如何避免僵尸进程产生?
    * 父进程先退出
    * 父进程处理子进程退出状态
    * 创建二级子进程
     
    进程池技术:
        产生原因:
        如果有大量的任务需要多进程完成,而调用周期比较短且需要频繁创建
    此时可能产生大量进程频繁创建销毁的情况  消耗计算机资源较大
        使用方法:
        1.创建进程池,在池内放入适当数量的进程
    2.将事件封装成函数。放入到进程池
    3.事件不断运行,直到所有放入进程池事件运行完成
    4.关闭进程池,回收进程
    同步互斥机制
        目的:
           解决对共有资源产生的资源争夺
        临界资源:
             多个进程或线程都可以操作的资源
        临界区:
            操作临界资源的代码段
        同步:
        同步是一种合作关系,为完成某个任务,
    多进程或者多个线程之间形成的一种协调
    按照约定执行,相互告知,共同完成任务
        互斥:
        互斥是一种制约关系,当一个进程或者线程
    进入临界区操作资源时采用上锁的方式,
    阻止其他进程操作,直到解锁后才能让出资源
    多线程:
        什么是线程(thread)?
          线程也是一种多任务编程方式,可以使用计算机的多核资源
          线程被称为轻量级的进程
        线程的特征:
          1.一个进程可以包含多个线程
          2.线程是计算机内核使用的最小单位
          3.线程也是一个运行过程,也要消耗计算机资源
          4.多个线程共享共用进程的资源
          5.线程也有自己的特征属性,TID、指令集、线程栈
          6.多个线程之间独立运行互不干扰 空间不独立(都消耗进程空间)
          7.线程的创建删除消耗的资源要小于进程 线程/进程(1/20)
    线程通信:
                   多个线程共用线程空间,所以进程的全局变量对进程内线程均可见
                   线程的通信方法就是使用去全局变量通信
           注:
                  线程间使用全局变量进程通信时,全局变量为共享资源
                   往往需要同步互斥机制
     
     
     
    进程和线程的区别和联系:
        1.两者都是多任务编程的方式  都能够使用计算机的多核
        2.进程的创建和删除要比线程消耗更多的计算机资源
        3.进程空间独立,数据安全性好,有专门的进程间的通信方法
        4.线程使用全局变量,更加简单,但需要同步互斥操作
        5.一个进程可以包含多个线程,线程共享进程空间资源
        6.进程线程都独立执行,有自己的特有属性
     
    使用情况:
        1.一个进程中并发任务比较多,比较简单,适合使用多线程
        2.如果数据程序比较复杂,特别是可能多个任务通信比较多的时候
          要考虑使用线程同步互斥的复杂性
        3.多个任务存在明显差异,和功能分离的时候没有必要一定写入到一个进程中
        4.使用Python要考虑到GIL的问题
     
     
    Pyhthon线程GIL问题:
        GIL (全局解释器锁)
        Python --->支持线程操作--->出现IO同步互斥--->加锁--->超级锁,给解释器加锁
        后果:
            同一时刻一个解释器只解释一个线程 
            此时其他线程需要等待。大大降低了Python线程的执行效率
            只能实现并发不能实现并行
     
    Python GIL问题解决方案:
        1.修改c解释器
        2.尽量使用多进程进行并行操作
        3.Python线程尽量用在高延迟多阻塞的IO情形
        3.不使用CPython   使用C#、JAVA 做的得解释器
     
     
    网络服务器基础:
        循环服务器::
            单进程程序,循环接受客户请求,处理请求,处理完毕后再接受下一次请求
            特点:
                每次只能处理一个客户端请求,如果客户端长期占有服务器则无法处理其他客户端
                请求
            优点:
                实现简单,占用资源少
            缺点:
                无法同时处理多个客户端,体验差
            使用情况:
                任务短暂,可以快速完成,udp比tcp更适合循环
        并发服务器:
            能够同时处理多个客户端任务请求
            IO并发:
                IO多路复用 协程
                优点:
                    可以实现IO并发操作,占用系统资源少
                缺点:
                    不能够监控CPU密集的情况,并不能有长期阻塞
            多进程/线程并发:
                为每个客户端单独提供一个进程或线程,处理客户端请求
                优点:
                    客户端可以长期占有服务器
                缺点:
                    消耗计算机资源比较多

     

  • 相关阅读:
    Django框架介绍
    CSS基础
    BOM与DOM操作
    JavaScript
    PHP Socket编程 之 fsockopen链接https时OpenSSL错误
    PHP Socket编程 之 深入解析fsockopen与pfsockopen的区别
    PHP Socket编程 之 使用fsockopen伪造IP
    PHP Socket编程 之 利用 fsockopen() 函数开放端口扫描器的实例
    PHP Socket编程 之 fsockopen指定ip获取远程数据 及 301自动跳转
    PHP Socket编程 之 抓取数据遇到Transfer-Encoding chunked
  • 原文地址:https://www.cnblogs.com/ParisGabriel/p/9499301.html
Copyright © 2011-2022 走看看