zoukankan      html  css  js  c++  java
  • python自动化开发学习 I/O多路复用

    python自动化开发学习 I/O多路复用

     

    一. 简介

      socketserver在内部是由I/O多路复用,多线程和多进程,实现了并发通信。IO多路复用的系统消耗很小。

    IO多路复用底层就是监听socket对象内部是否有变化,是否在收发消息,Python中select模块提供了select poll epoll 三种方式来实现IO多路复用,支持不同的操作系统。

      windows : 提供select

      Mac : 提供select

      Linux : 提供select, poll, epoll

    (1)select简介:

      select 默认会将每一个变化的socket加入一个他自己维护的列表中,但是并不会明确是哪一个socket,它内部其实是通过一个for循环遍历整个列表,当列表中的socket很多的时候,for循环就会浪费资源。select默认可以监听1024个链接,它是由linux在同文件中定义的,可修改。select不是安全的线程,如果你把一个socket加入到select中,突然另外一个线程关闭了这个socket,那么接下来select的行为是随机的。

    (2)poll简介:

      poll修复了select的一些问题,例如去掉了1024个链接限制,想要监听多少都可以,poll不在修改传入的数组,但是poll依然不是线程安全的。

    (3)epoll简介:

      epoll不仅会告诉你socket组里面的数据,还会告诉你具体哪个socket有数据,无需自己寻找。而且epoll终于是线程安全的了。然而epoll局限于

    Linux操作系统。

    二 . 应用

    select 方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)<br>
    参数: 可接受四个参数(前三个必须)
    返回值:三个列表
       
    select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
    1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
    2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
    3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
    4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
       当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

    (1)监听客户端连接

     服务端

    import socket
    import select

    sk = socket.socket()
    sk.bind(("127.0.0.1", 9999,))
    sk.listen(5)

    while True:
    rlist, w, e = select.select([sk, ], [], [], 5)
    # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk]
    # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端]
    print(rlist)

    for r in rlist:
    print(r)
    conn, address = r.accept()
    conn.sendall(bytes("hello", encoding="utf-8"))

    服务端

     客户端

    import socket

    sk = socket.socket()
    sk.connect(("127.0.0.1", 9999,))

    data = sk.recv(1024)
    print(data)

    while True:
    input(">>")

    sk.close()

    客户端

    (2)监听客户端连接及接受客户端发送的数据

     服务端

    import socket
    import select

    sk = socket.socket()
    sk.bind(("127.0.0.1", 9999,))
    sk.listen(5)


    inputs = [sk,]
    while True:
    rlist, w, e = select.select(inputs, [], [], 1)
    # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk]
    # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端]

    print(len(inputs), len(rlist))

    for r in rlist:
    print(r)
    if r == sk:
    # 有新的客户端连接进来
    conn, address = r.accept()
    inputs.append(conn)
    conn.sendall(bytes("hello", encoding="utf-8"))
    else:
    # 有客户端给我发送数据
    recv_data = r.recv(1024)
    print(recv_data)

    服务端

     客户端

    import socket

    sk = socket.socket()
    sk.connect(("127.0.0.1", 9999,))

    data = sk.recv(1024)
    print(data)

    while True:
    inp = input(">>")
    sk.sendall(bytes(inp, encoding='utf-8'))

    sk.close()

    客户端

    (3)读写分离

     服务端

    import socket
    import select

    sk = socket.socket()
    sk.bind(("127.0.0.1", 9999,))
    sk.listen(5)


    inputs = [sk,]
    outputs = []

    while True:
    rlist, wlist, e = select.select(inputs, outputs, [], 1)
    # 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk]
    # 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端]

    print(len(inputs), len(rlist))

    for r in rlist:
    print(r)
    if r == sk:
    # 有新的客户端连接进来
    conn, address = r.accept()
    inputs.append(conn)
    conn.sendall(bytes("hello", encoding="utf-8"))
    else:
    # 有客户端给我发送数据
    try:
    recv_data = r.recv(1024)

    if not recv_data: # 某些操作系统,在socket断开的时候,会发送一个空给server,所以要判断空的情况
    raise Exception("断开连接")
    else:
    outputs.append(r)
    except Exception as e:
    inputs.remove(r)

    for w in wlist:
    w.sendall(bytes("response", encoding="utf-8"))
    outputs.remove(w)

    服务端

     客户端

    import socket

    sk = socket.socket()
    sk.connect(("127.0.0.1", 9999,))

    data = sk.recv(1024)
    print(data)

    while True:
    inp = input(">>")
    sk.sendall(bytes(inp, encoding='utf-8'))
    print(sk.recv(1024))
    sk.close()

    客户端

    (4)读写分离之服务端根据客户端的输入返回对应的信息

     服务端

    import socket
    import select

    sk = socket.socket()
    sk.bind(("127.0.0.1", 9999,))
    sk.listen(5)


    inputs = [sk, ] #
    outputs = []
    messages = {} # 用来存放客户端发送过来的消息,存放格式 连接客户端的对象: 消息

    while True:
    rlist, wlist, elist = select.select(inputs, outputs, [sk, ], 1)
    # rlist 监听sk(服务器端)对象,如果sk对象发生变化,表示有客户端来连接了,此时rlist值为[sk]
    # wlist 监听conn对象,如果conn发生变化,表示客户端有消息发送过来了,此时rlist的值为[客户端]
    # elist 监听sk(服务器端)对象是否发生错误

    print(len(inputs), len(rlist))

    for r in rlist:
    print(r)
    if r == sk:
    # 有新的客户端连接进来
    conn, address = r.accept()
    inputs.append(conn)
    messages[conn] = [] # 在存储消息的字典中创建一个以客户端连接对象为key 空列表为value的键值对
    conn.sendall(bytes("hello", encoding="utf-8"))
    else:
    # 有客户端给我发送数据
    try:
    recv_data = r.recv(1024)

    if not recv_data: # 某些操作系统,在socket断开的时候,会发送一个空给server,所以要判断空的情况
    raise Exception("断开连接")
    else:
    outputs.append(r)
    messages[r].append(recv_data)

    except Exception as e:
    inputs.remove(r)
    del messages[r]

    for w in wlist:
    print(messages[w])
    msg = messages[w].pop()
    resp = msg + bytes("response", encoding="utf-8")
    w.sendall(resp)
    outputs.remove(w)

    服务端

     客户端

    import socket

    sk = socket.socket()
    sk.connect(("127.0.0.1", 9999,))

    data = sk.recv(1024)
    print(data)

    while True:
    inp = input(">>")
    sk.sendall(bytes(inp, encoding='utf-8'))
    print(sk.recv(1024))
    sk.close()

    客户端

      

  • 相关阅读:
    Oracle 导入导出 创建用户等
    如何导出 Windows EventLog
    QT connect 的信号,不能写类名
    Easylogging
    Openstack Swift SLO & bulk delete 测试常用命令,文件等
    Openstack Swift Static Large Object (SLO)
    Linux 创建指定大小的文件
    Openstack Swiftclient 查看 log
    Openstack Swift 批量删除 (bulk delete)
    winsock server 示例代码中 shutdown 的选项
  • 原文地址:https://www.cnblogs.com/meng-wei-zhi/p/8258848.html
Copyright © 2011-2022 走看看