IO多路复用

 内容回顾:

第一阶段:
socket, 服务端同时只处理一个请求
第二阶段:
select + socket, 实现伪并发
a. r_list : 即读又写
b. r_list, w_list 读写分离
第三阶段:
socketserver
select / epoll + socket +多线程
并发操作

IO多路复用:通过一种机制,可以监视多个描述符(文件句柄,对象的标识符, 定位文件数据在内存中位置),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Python中select模块,提供:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

基于select实现多路复用功能:

server端:
import socket sk1 = socket.socket() sk1.bind(("127.0.0.1",8001,)) sk1.listen() sk2 = socket.socket() sk2.bind(("127.0.0.1",8002,)) sk2.listen() sk3 = socket.socket() sk3.bind(("127.0.0.1",8003,)) sk3.listen() inputs = [sk1,sk2,sk3] import select while True: #sk1,sk2, select在内部自动监听多个对象 #如果有人连接 sk1 #r_list = [sk1,sk2],第一个inputs:如果对象发生变化就放到r_list里, #中间的参数传了什么:w_list就是什么,不会变动 #第三个inputs:如果谁发生错误就放到e_list里面 r_list,w_list,e_list = select.select(inputs,[sk1,sk2,],inputs,1)#1为等待时间 for sk in r_list: #每一个连接对象 conn,address = sk.accept() conn.sendall(bytes("hello",encoding="utf-8")) conn.close() for sk in e_list: inputs.remove(sk)#谁出错就把谁去掉 clint端1: import socket obj = socket.socket() obj.connect(("127.0.0.1",8001,)) connent = str(obj.recv(1024), encoding="utf-8") print(connent) obj.close() clint端2: import socket obj = socket.socket() obj.connect(("127.0.0.1",8002,)) connent = str(obj.recv(1024), encoding="utf-8") print(connent) obj.close()

完整的server端和client端示例(伪并发)

实现了一个server,其功能就是可以和多个client建立连接,每个client的发过来的数据加上一个response字符串返回给client端~~~

server端:
import socket
import select

sk = socket.socket()
sk.bind(('127.0.0.1', 9000),)
sk.listen(5)

inputs = [sk, ]
outputs = []
message = {}  # 实现读写分离
print("start...")

while True:
    # 监听的inputs中的socket对象内部如果有变化,那么这个对象就会在rlist
    # outputs里有什么对象,wlist中就有什么对象
    # []如果这里的对象内部出错,那会把这些对象加到elist中
    # 1 是超时时间
    rlist, wlist, elist = select.select(inputs, outputs, [], 1)
    print(len(inputs), len(outputs))

    for r in rlist:
        if r == sk:
            conn, addr = sk.accept()
            conn.sendall(b"ok")
            # 这里记住是吧conn添加到inputs中去监听,千万别写成r了
            inputs.append(conn)
            message[conn] = []
        else:
            try:
                data = r.recv(1024)
                print(data)
                if not data:
                    raise Exception('连接断开')
                message[r].append(data)
                outputs.append(r)
            except Exception as e:
                inputs.remove(r)
                del message[r]

    for r in wlist:
        data = str(message[r].pop(), encoding='utf-8')
        res = data + "response"
        r.sendall(bytes(res, encoding='utf-8'))
        outputs.remove(r)
# 实现读写分离
# IO多路复用的本质是用select、poll、epoll(系统底层提供的)来监听socket对象内部是否有变化
# select 是在Win和Linux中都支持额,相当于系统内部维护了一个for循环,缺点是监听个数有上限(1024),效率不高
# poll的监听个数没有限制,但仍然用循环,效率不高。
# epoll的机制是socket对象变化,主动告诉epoll。而不是轮询,相当于有个回调函数,效率比前两者高
# Nginx就是用epoll。只要IO操作都支持,除开文件操作

# 列表删除指定元素用remove


clint端:
import socket
sc = socket.socket()
sc.connect(("127.0.0.1", 9000,))

data = sc.recv(1024)
print(data)
while True:
    msg = input(">>>:")
    if msg == 'q':
        break
    if len(msg) == 0:
        continue

    send_msg = bytes(msg, encoding="utf-8")
    sc.send(send_msg)
    res = sc.recv(1024)
    print(str(res, encoding="utf-8"))
sc.close()

线程:

import threading
import time
def process(arges):
    time.sleep(1)
    print(arges)

#轮循
# for i in range(10):
#     process(i)

#创建10个线程,一次执行这10个
for i in range(10):
   t = threading.Thread(target=process, args=(i,))#创建线程,每创建一个线程让一个方法去执行它
   t.start()

进程

协程

成产者消费模型

原文地址:https://www.cnblogs.com/zcok168/p/9276889.html