首页 > 代码库 > 39.IO多路复用(用select实现伪并发)

39.IO多路复用(用select实现伪并发)

IO多路复用

1.用select实现多端口被多客户端访问的多路复用伪并发

  IO多路复用服务端:既读又写

# IO多路复用实现伪并发   用多个IO,可以监听多个文件句柄(socket对象)(一般是可以读了或者可以写了),
# 一旦文件句柄出现变化,就可以感应到
# 对于原生的socket  只能处理一个请求,只能监听一个端口
# 1.如何让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 = [sk3, sk1, sk2, ]

import select
# select,内部c语言实现,for循环监听多个句柄 最大1024个(window上只支持select)
# poll,内部依然用for循环检测无限制
# epool内部使用异步(谁有变化,谁告诉我,不一个一个去问),内部让每个句柄发生变化的时候去告知epoll,不用循环

while True:
    r_list, w_list, e_list = select.select(inputs, [], [], 1)
    ‘‘‘
    检测句柄变化
    参数1:1为等待时间  1s后进去下次循环
    第三个参数:感知句柄错误,从inputs传到e_list,并将错误句柄从第一个参数中去除,程序就不会发生错误
    第二个参数:参数中的值,均会传到w_list,无论其是否发生变化
    第一个参数:检测句柄变化,inputs中谁变化就将其放入r_list
    ‘‘‘
    # [sk3, sk1, sk2]  内部自动监听sk3,sk1, sk2三个对象,一旦某个句柄发生变化
    # 如果有人来连sk3,r_list = [sk]接收客户端的请求
    # 如果有人来连sk1,r_list = [sk1]接收客户端的请求
    for sk in r_list:
        # 每一个连接对象
        conn, address = sk.accept()
        conn.sendall(bytes(‘hello‘,encoding=‘utf-8‘))
        conn.close()

  客户端1:

import socket

obj = socket.socket()

obj.connect((‘127.0.0.1‘,8001,))

content = str(obj.recv(1024),encoding=‘utf-8‘)

print(content)

obj.close()

  客户端2:

import socket

obj = socket.socket()

obj.connect((‘127.0.0.1‘,8002,))

content = str(obj.recv(1024),encoding=‘utf-8‘)

print(content)

obj.close()

 

2.用select实现,多个客户端的不同端口访问服务器的某一端口的伪并发,读写分离

  服务端:

import socket

sk1 = socket.socket()
sk1.bind((‘127.0.0.1‘, 8001,))
sk1.listen()

inputs = [sk1, ]
outputs = []  # outputs的内容将会一直存在于w_list中
message = {}

import select

# select监听socket对象,一旦socket变换就能感知到
‘‘‘
服务端:
sk1 =socket.socket()
conn, address = sk1.accept()
conn.recv(1024)
客户端:
sk1 = socket.socket()
sk1.sendall()

在服务端对象sk1只负责接收用户的连接

而conn才负责专门与每一个客户端的sk1对象通信

‘‘‘

while True:
    # 如果有人连接sk1
    # r_list = [sk1,]
    # 如果有人第一次连接,sk1发生变化
    r_list, w_list, e_list = select.select(inputs, outputs, inputs, 1)
    print(‘正在监听的socket对象%d‘ % len(inputs))
    print(r_list)


    for sk1_or_conn in r_list:
        # 每一个连接对象
        if sk1_or_conn == sk1:
            # 表示有新用户来连接
            conn, address = sk1_or_conn.accept()
            inputs.append(conn)  # inputs = [sk1,conn]
            message[conn] = []
        else:
            try:  # 有老用户发消息了
                ‘‘‘
                2.7中,客户端断开连接,服务器会接收到空字符串
                3.6中,断开后,服务器会报错
                ‘‘‘
                data_bytes = sk1_or_conn.recv(1024)
            except Exception as ex:
                inputs.remove(sk1_or_conn)
                # 从inputs中移除,不再联系
            else:
                data_str = str(data_bytes, encoding=‘utf-8‘)
                outputs.append(sk1_or_conn)  #w_list记录哪个客户端跟服务器发送过消息
                message[sk1_or_conn].append(data_str )


# 本质上还是一个一个处理。只是事情一发生即可处理而已
# select的难点在于它的第一个参数,既放了服务端的socket对象,
                # 还放了客户端与服务端的通道对象conn

    # w_list仅仅保存了谁给我发过消息
    for conn in w_list:
        recv_str = message[conn][0]
        del message[conn][0]
        conn.sendall(bytes(recv_str + ‘好‘, encoding=‘utf-8‘))
        outputs.remove(conn)

  客户端:(在python中按快捷键shift+crtl+F10模拟不同的客户端)  

import socket

obj = socket.socket()
obj.connect((‘127.0.0.1‘, 8001,))

while True:
    inp = input(‘>>>‘)
    obj.sendall(bytes(inp, encoding=‘utf-8‘))
    ret = str(obj.recv(1024), encoding=‘utf-8‘)
    print(ret)

  

  

  

  

39.IO多路复用(用select实现伪并发)