zoukankan      html  css  js  c++  java
  • Python网络编程(http协议,IO多路复用、select内核监听)

    前言:

    什么是IO?
    • 分为IO设备和IO接口两个部分
    • Linux系统,I/O操作可以有多种方式
    • 比如DIO(DirectI/O)
    • AIO(AsynchronousI/O异步I/O)
    • Memory-MappedI/O(内存映设I/O)等...
    • 不同的I/O方式有不同的实现方式和性能,在不同的应用中可以按情况选择不同的I/O方式。

    补充昨天HTTP:


    from socket import *
    # 接收请求
    # 查看请求
    # 返回客户端段请求内容
    
    
    def handleClient(connfd):
        request = connfd.recv(4096)
        # print("***********")
        # print(request)
        # print("************")
        # 按照行切割请求
        request_lines = request.splitlines()
        for line in request_lines:
            print(line.decode())
    
        try:
            f = open('index.html')
        except IOError:
            response = "HTTP/1.1 303 Not Found
    "
            response += "
    "  # 空行
            response += '''
                    **************************
                    Sorry, not found the page.
                    **************************
                    '''
        else:
            response = "HTTP/1.1 200  OK
    "
            response += '
    '
            response += f.read()
        finally:
            # 发送给浏览器
            connfd.send(response.encode())
    
    
    # 创建套接字,调用handleClient完成功能
    def main():
        # 创建tcp套接字
        sockfd = socket()
        sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        sockfd.bind(('0.0.0.0', 8000))
        sockfd.listen()
        while True:
            print("Listen the port 8000...")
            connfd, addr = sockfd.accept()
            # 处理浏览器发来的请求
            handleClient(connfd)
            connfd.close()
    
    
    if __name__ == "__main__":
        main()
    S.splitlines  
    拆分文件字符串按行分隔
     
     
     
     
    内存中存在数据交换的操作认为是IO操作(输入输出)
    例如:
        内存与磁盘数据交换:文件读写、数据库更新
        内存和终端数据交换:input、print、sys.stdout、sys.stdin、  sys.stder
        内存和网络数据交换:网络连接、recv、send、recvfrom
     
    IO秘集程型序:
         程序执行中有大量的IO操作,而比较少的CPU运算操作
         消耗CPU较少,IO运行时间长
    cpu(计算)密集形程序:
         程序存在大量的CPU运算,IO操作相对较少
         CPU消耗大
     
    IO分类:
        1.阻塞IO:
            程序运行中遇到IO条件没有达成
    或传输情况较慢的情况下会出现阻塞状态
      阻塞IO是IO最简单的逻辑情形,也是默认状态
      阻塞IO是效率很低的IO状态
    阻塞情况:
        1.因为IO条件没有达成
            IO阻塞函数(input、print、recv、recvfrom)
        2.处理IO耗时较长形参阻塞
            文件读写、网络数据发送过程
     
        2.非阻塞IO:
            在程序运行中遇到IO操作不让其产生阻塞
    实现手段:
       改变IO事件的属性,使其变为非阻塞
       通常会和循环一起使用 进行条件的循环监控
     
        3.IO多路复用
            定义:
      通过一个监测,可以同时监控多个IO事件的行为
      当那个IO可以执行,让这个IO事件发生
      同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件
      此时形成多个IO时间都可以操作的现象,不必逐个等待执行
    IO准备就绪:
      IO事件即将发生时的临界状态不可逆转
      在程序中存在的IO事件中选择要监测的事件
      创建监测,将监测的IO事件注册
      等待监测的IO事件发生判断是什么事件
      处理相应的IO
     
     
        事件驱动IO
        异步IO
        ...
     
    s.setblocking(False)
       功能:
          将套接字设置为非阻塞状态
       参数:
          默认为阻塞, 设置为False为非阻塞状态
     
    超时检测:
        原本阻塞的IO设置一个最长阻塞等待时间
        在规定时间内如果达到条件正常执行
        如果时间到仍未达到条件则结束阻塞
            s.settimeout(sec)
                功能:
                        设置套接字的超时时间
                 参数:
                       时间(
     
     
    select模块
    from select import
    select 支持:Windows、Linux、Unix  
    poll   支持:Linux、Unix
    epoll  支持:Linux、Unix
     
    rs, ws, xs = select(rlist, wlist, xlist[, timeout])
       功能:
          监控IO事件 ,阻塞等待监控的IO事件发生
       参数:
           rlist   列表:
              表示存放我们需要等待处理的IO
           wlist   列表:
              表示存放我们想要主动处理的IO
           xlist   列表:
              表示存放出错希望去处理的IO
           timeout: 超时检测
       返回值:
          rs  列表:
              准备就绪的IO
          ws  列表:
              准备就绪的IO
          xs  列表
              准备就绪的IO
     
    * 在处理IO时不要形成死循环会让一个客户端单独占有服务端
     
    IO多路复兴形成一种可以同时处理多个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.做标志位的过滤
     
     
     
    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事件
     
    poll方法实现IO多路复用:
    from socket import * 
    from select import *
    
    s = socket()
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(('0.0.0.0',8888))
    s.listen(5)
    
    #创建poll对象
    p = poll()
    
    #创建地图
    fdmap = {s.fileno():s}
    
    #添加关注
    p.register(s,POLLIN | POLLERR)
    
    while True:
        #进行IO监控
        #[(fileno,evnet),...]
        events = p.poll()
        for fd,event in events:
            if fd == s.fileno():
                #从地图中找到fd对应的对象
                c,addr = fdmap[fd].accept()
                print("Connect from",addr)
                #注册新的IO 维护地图
                p.register(c,POLLIN)
                fdmap[c.fileno()] = c 
            else:
                data = fdmap[fd].recv(1024)
                if not data:
                    p.unregister(fd) #从关注移除
                    fdmap[fd].close()
                    del fdmap[fd]  #从地图删除
                else:
                    print(data.decode())
                    fdmap[fd].send('收到了'.encode())
     
     
    select IO多路复用服务器端:
     
    from socket import *
    from select import select
    # 创建套接字
    s = socket()
    # 设置端口重用
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # 绑定地址
    s.bind(('0.0.0.0', 8888))
    # 设置队列
    s.listen(5)
    # 注册监听套接字
    rlist = [s]
    wlist = []
    xlist = [s]
    
    while True:
        print("等待IO发生")
        rs, ws, xs = select(rlist, wlist, xlist)
        # 循环遍历rs准备就绪列表
        for r in rs:
            # 如果有新的客户端链接请求
            if r is s:
                # 链接客户端并返回客户端套接字
                connfd, addr = r.accept()  # r==s 原套接字
                print("Connect from", addr)
                # 将绑定客户端套接字加入监听列表
                rlist.append(connfd)
            # 表示客户端连接套接字准备就绪
            else:
                # 如果是客户端套接字发送数据 则接收
                data = r.recv(1024)
                if not data:  # 如果客户端断开链接
                    # 从关注列表移除connfd
                    rlist.remove(r)
                    r.close()  # 关闭套接字
                else:
                    print("Receive:", data.decode())
                    # 讲客户端套接字放入wlist
                    wlist.append(r)
                    # wlist列表会直接返回
        # 循环遍历ws准备就绪列表
        for w in ws:
            # 消息回复
            w.send("这是一条回复消息".encode())
            # 删除并取消监听已处理消息
            wlist.remove(w)
        # xs列表:待处理异常
        for x in xs:
            if x is s:
                s.close()


     
     
     
     
    sys.stdin
     
     
     
     
     
    客户端:
     

    from socket import *
    
    #  创建套接字
    sockfd = socket()
    
    #  发起连接
    sockfd.connect(('127.0.0.1', 8888))
    
    while True:
        #  消息收发
        msg = input("Msg>>")
        if not msg:
            break
        sockfd.sendall(msg.encode())
        data = sockfd.recv(1024)
        print(data.decode())
    
    sockfd.close()

    应用 :
    select服务端,同时关注客户端连接
    客户端发送和终端输入。将客户端发送和终端输入的内容全都写入到一个文件中
     

    # myserver.py
    # 应用 :
    # select服务端,同时关注客户端连接
    # 客户端发送和终端输入。将客户端发送和终端输入的内容全都写入到一个文件中
    
    from socket import *
    from select import *
    from sys import stdin
    
    sock = socket()
    sock.getsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    sock.bind(("127.0.0.1", 6666))
    sock.listen(5)
    rlist = [sock, stdin]
    wlist = []
    xlist = []
    file = open("selectdemo.txt", "w+b")
    while True:
        rs, ws, xs, = select(rlist, wlist, xlist)
        for r in rs:
            if r == sock:
                connfd, addr = r.accept()
                print("已连接......")
                rlist.append(connfd)
            elif r == stdin:
                data = stdin.readline()
                file.write(data.encode())
                file.flush()
            else:
                data = r.recv(4096)
                if not data:
                    rlist.remove(r)
                    r.close()
                else:
                    file.write(data + "
    ".encode())
                    file.flush()
                    print("已经接收内容并写如select>>>.txt文件内
     内容:", data.decode())
                    r.send("接收成功!".encode())
    file.close()


    
    
    from socket import *
    
    #  创建套接字
    sockfd = socket()
    
    #  发起连接
    sockfd.connect(('127.0.0.1', 6666))
    
    while True:
        #  消息收发
        msg = input("Msg>>")
        if not msg:
            break
        sockfd.sendall(msg.encode())
        data = sockfd.recv(1024)
        print(data.decode())
    
    sockfd.close()


     
     
     
  • 相关阅读:
    强化学习的基本迭代方法
    基于文本描述的事务聚类
    学习强化学习之前需要掌握的3种技能
    其它 华硕 ASAU S4100U 系统安装 win10安装 重装系统 Invalid Partition Table 解决
    数据分析 一些基本的知识
    Python 取样式的内容 合并多个文件的样式 自定义样式
    电商 Python 生成补单公司需要的评论格式3
    SpringBlade 本地图片上传 生成缩略图
    SQL Server 字符串截取
    SpringBlade 本地图片上传
  • 原文地址:https://www.cnblogs.com/ParisGabriel/p/9446617.html
Copyright © 2011-2022 走看看