IO多路复用,简单理解就是一个客户端可以同时访问多个服务器端口,一个服务器也可以同时接受多个客户端的访问。

  多路复用的方式有三种:select、pool、epoll,Windows只支持select方式。

  select、pool内部使用的是for循环检测,select最多监听1024个、pool监听个数不限。epoll是异步方式,不使用循环监听,谁有变化的时候来通知我。

  这里记录的是基于select实现IO多路复用

  一、一个客户端口、多个服务器端口;读写未分离

  服务器端同时开放多个端口等待访问,循环监听各端口情况,当有服务器端口被访问时,建立连接并开始通信。

  服务器端:

import socket
import select

# 服务器端口1:8088
sk1 = socket.socket()
sk1.bind(('192.168.1.103',8088,))
sk1.listen(5)

# 服务器端口2:8008
sk2 = socket.socket()
sk2.bind(('192.168.1.103',8008,))
sk2.listen(5)

# 服务器端口3:8080
sk3 = socket.socket()
sk3.bind(('192.168.1.103',8080,))
sk3.listen(5)

# 开放的服务器端句柄
inputs = [sk1,sk2,sk3,]
outputs = []
time = 1

while True:
    # 循环监听各端口
        # r_list:监听到的有变换的端口句柄
        # w_list与outputs相对应,输入什么、输出什么。
        # e_list:监听到的又异常或断开连接的端口句柄
        # inputs:被监听的端口句柄列表
        # time:监听一次的最长时间间隔
    r_list , w_list , e_list = select.select(inputs,outputs,inputs,time)
    print('被监听的对象:%d个' % len(inputs))

    # 循环处理被访问的端口
    for sk in r_list:
        # 建立连接,创建连接句柄conn
        conn,address = sk.accept()
        # 发送欢迎消息至客户端
        conn.sendall(bytes('hello...',encoding='utf-8'))
        # 接收服务的消息后关闭连接
        response = conn.recv(1024)
        print(response)
        conn.close()
        # 删除对服务器端口的监听
        inputs.remove(sk)
View Code

  客户端:

import socket
s1 = socket.socket()
# 建立连接
s1.connect(('192.168.1.103',8008,))
while True:
    data = input('>>>')
    if data == 'q':
        break
    else:
        # 接收信息
        response = str(s1.recv(1024),encoding='utf-8')
        print(response)
        # 发送消息
        s1.sendall(bytes(data,encoding='utf-8'))
# 切断连接
s1.close()
View Code

  二、一个服务端口、多个客户端口;读写分离

  使用for循环实现伪异步方式的IO多路复用,可同时处理多个客户端的访问请求、并建立连接开始通信。

  服务器端:

import socket

# 服务器端口开放
sk1 = socket.socket()
sk1.bind(('192.168.1.103',8088,))
sk1.listen()

import select
inputs_or_conn = [sk1,]
outputs = []
message_dict = {}


while True:
    # 循环监听各端口
    r_list,w_list,e_list = select.select(inputs_or_conn,outputs,inputs_or_conn,1)
    print('正在监听的对象:%d' % len(inputs_or_conn))
    print(r_list)

    for sk in r_list:

        # 新用户访问 ,建立连接
        if sk == sk1:
            conn,adress = sk.accept()
            # 登记并开始监听来访的socket
            inputs_or_conn.append(conn)
            # 创建列表,记录客户端发过来的信息
            message_dict[conn] = []

        # 已建立连接的用户继续访问
        else:
            try:
                # 接收客户端发送的消息
                data_bytes = sk.recv(1024)
            except Exception as ex:
                # 客户端出现异常或者退出,停止监听
                inputs_or_conn.remove(sk)
            else:
                # 将消息str存入客户端消息列表
                data_str = str(data_bytes,encoding='utf-8')
                message_dict[sk].append(data_str)
                # 客户添加至w_list等待反馈
                w_list.append(sk)

    for conn in w_list:
        # 进行消息回复,回复后conn从w_list中删除
        conn.sendall(bytes(message_dict[conn][0]+'hi...',encoding='utf-8'))
        w_list.remove(conn)
        # 将此消息从w_list中删除
        del message_dict[conn][0]
View Code

  客户端1:

import socket
s1 = socket.socket()
# 建立连接
s1.connect(('192.168.1.103',8008,))
while True:
    data = input('>>>')
    if data == 'q':
        break
    else:
        # 接收信息
        response = str(s1.recv(1024),encoding='utf-8')
        print(response)
        # 发送消息
        s1.sendall(bytes(data,encoding='utf-8'))
# 切断连接
s1.close()
View Code

  客户端2:

import socket
s2 = socket.socket()
# 建立连接
s2.connect(('192.168.1.103',8088,))

while True:
    data = input('>>>')
    if data == 'q':
        break
    else:
        # 发送消息
        s2.sendall(bytes(data,encoding='utf-8'))
        # 接收信息
        response = str(s2.recv(1024),encoding='utf-8')
        print(response)

# 切断连接
s2.close()
View Code