zoukankan      html  css  js  c++  java
  • Python网络编程:IO多路复用

    io多路复用:可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,即可感知。

      1 sk1 = socket.socket()
      2 sk1.bind(('127.0.0.1',8001))
      3 sk1.listen()
      4 
      5 # sk2 = socket.socket()
      6 # sk2.bind(('127.0.0.1',8002))
      7 # sk2.listen()
      8 while True:
      9     conn,address = sk.accept()#阻塞等待客户端连接、连接、客户端地址信息
     10     print(conn,address)
     11     conn.sendall(bytes('北京欢迎你',encoding='utf-8'))
     12     while True:
     13         ret_bytes = conn.recv(1024)
     14         ret_str = str(ret_bytes,encoding='utf-8')
     15         if ret_str == 'q':
     16             break
     17         conn.sendall(bytes(ret_str+'好',encoding='utf-8'))
     18     print(address,conn)
     19 

    sk1sk2就叫作文件描述符、文件句柄。上面的程序只能执行sk1。

    IO多路复用有一种机制,可以接受多个文件描述符,一旦有谁变化了,就处理。

      1 import socket
      2 sk1 = socket.socket()
      3 sk1.bind(('127.0.0.1',8001))
      4 sk1.listen()
      5 
      6 sk2 = socket.socket()
      7 sk2.bind(('127.0.0.1',8002))
      8 sk2.listen()
      9 
     10 sk3 = socket.socket()
     11 sk3.bind(('127.0.0.1',8002))
     12 sk3.listen()
     13 
     14 inputs = [sk1,sk2]
     15 #本例是用select伪装成多处理用户连接请求,比socket的好处在于不用等待? 难点在于inputs里两类socket,客户端socket和服务端socket?
     16 import select
     17 while True:
     18     #[sk1,sk2,],select内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化,就会将其放到r_list里。第一次发生变化是sk.accept(),即有人来连sk1.则r_list = [sk1]
     19     r_list,w_list,e_list = select.select(inputs,outputs,inputs,1)#等一秒看是否有人来连接,没有的话执行下一次循环。最多等待的时间。
     20     print('正在监听的socket对象%d' % len(inputs))
     21     print(r_list)
     22     for sk1_or_conn in r_list:
     23 

    IO多路复用是操作系统底层提供的功能,我们只是用Python去调用它,分三种方式,select,poll,epoll。windows只支持select.

    select底层实现原理:

    系统内部c语言进行for循环检测,当文件句柄发送变化的时候告诉我们。性能较低,并且只支持最多1024个文件描述符。


    所以后来就有了poll,对于文件描述符个数没限制了,但底层也是用for循环实现的。

    之后又出现了epoll,底层就不用for循环了,而是用异步实现的,把句柄都放进去,谁有变化了谁主动告诉epoll,而不是for循环一遍遍监测。所以epoll的性能是最高的。

    Nginx的内部就是socket结合epoll来监听用户请求的。

    for sk in e_list:  #e_list是发生错误的文件描述符列表

        inputs.remove(sk)


      1 import socket
      2 sk1 = socket.socket()
      3 sk1.bind(('127.0.0.1',8001))
      4 sk1.listen()
      5 
      6 # sk2 = socket.socket()
      7 # sk2.bind(('127.0.0.1',8002))
      8 # sk2.listen()
      9 #
     10 # sk3 = socket.socket()
     11 # sk3.bind(('127.0.0.1',8003))
     12 # sk3.listen()
     13 inputs = [sk1]
     14 outputs = []
     15 #本例是用select伪装成多处理用户连接请求,比socket的好处在于不用等待? 难点在于inputs里两类socket,客户端socket和服务端socket。
     16 import select
     17 while True:
     18     #[sk1,sk2,],select内部自动监听sk1,sk2,sk3三个对象,一旦某个句柄发生变化
     19     r_list,w_list,e_list = select.select(inputs,outputs,inputs,1)
     20     print('正在监听的socket对象%d' % len(inputs))
     21     print(r_list)
     22     for sk1_or_conn in r_list:
     23         #每一个连接对象
     24         if sk1_or_conn == sk1:
     25             #表示有新用户来连接
     26             conn, address = sk1_or_conn.accept()
     27             inputs.append(conn)
     28         else:
     29             #有老用户发消息了
     30             try:
     31                 data_bytes = sk1_or_conn.recv(1024)
     32             except Exception as ex:
     33                 #如果用户中断连接
     34                 inputs.remove(sk1_or_conn)
     35             else:
     36                 #用户正常发消息
     37                 # data_str = str(data_bytes,encoding='utf-8')
     38                 # sk1_or_conn.sendall(bytes(data_str+'好',encoding = 'utf-8'))
     39                 outputs.append(sk1_or_conn)
     40 #w_list仅仅存谁给我发过消息,如果想读写分离就会用到这个参数
     41     for conn in w_list:
     42         conn.sendall(bytes('hello',encoding='utf-8'))
     43         outputs.remove(conn)
     44 
    IO多路复用处理多用户请求

    上面的难点在于要理解r_list其实并不等于inputs.

    inputs里面两类数据,一类是服务端的socket sk1,另一类是客户端的socket(只要有人来连接就apend一个socket对象)

    而r_list里面存的是发生变化的对象,多个用户来连sk1,r_list里就是sk1,而有用户发消息,那r_list里就变为发消息的socket对象其。


    socketserver:
    select/epoll + socket +多线程实现并发操作。

  • 相关阅读:
    ubuntu-18.04自动配置脚本
    Nodejs on windows 10
    终端接收FFMEPG推送的流出现音频卡顿问题
    FFMPEG 4.0 版本 支持PSI设置
    FFMPEG 支持https协议
    FFmpeg修改AC3编码的描述子
    FFMPEG 设置socket缓冲区
    将 h264 格式转换为YUV数据
    将 YUV 数据 编码为 h.264 格式
    将 PCM 数据编码为AAC格式
  • 原文地址:https://www.cnblogs.com/bolenzhang/p/7197898.html
Copyright © 2011-2022 走看看