在学习和了解这个模块之前,先要搞清楚几个概念:阻塞IO 非阻塞IO 和多路复用IO

阻塞IO

当有数据传入传出的时候,接收方必须一直等到接收到数据才能进入到下一步的操作。在接收数据之前,会一直进行等待,这时候是一个阻塞的状态。在socket模块默认的情况下实现的server端 就是一个阻塞IO的例子

非阻塞IO

与阻塞IO相对,在接收方等待数据的时候,如果发送方没有发送数据,接收方也可以进行后面的操作,等待对方将数据发送过来再执行之前的操作。具体实现在等待数据的时候,先执行后面的程序,按照一定的时间,反复的去查看,对方是否已经发送了数据。

通过socket模块 也可以实现server和client 非阻塞IO的例子:

server端 代码:


import time
import socket

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(('127.0.0.1',6666))
sk.listen(5)
sk.setblocking(False) #开启非阻塞状态
print("Waiting for lient connecting ...")

while True:
    try:
        conn,addr = sk.accept() # 进入主动轮询
        print("+++",addr)
        client_message = conn.recv(1024)
        print(client_message.decode())
        conn.close()
    except Exception as e: #当没有连接过来时 执行如下操作
        print(e)
        time.sleep(4)

执行效果如下,当没有连接过来时,进入except语句 打印异常信息

42214856.png

在终端下,利用nc发送数据,并server接收并打印 达到一个非阻塞IO的效果

42234873.png

多路复用IO

在学习select模块前,还需要清楚多路复用IO的概念
IO多路复用 是IO模式的一种,是一种单线程处理多并发IO操作的方案。IO多路复用,其实就是常说的 selectpollepoll
它的基本原理就是 这三个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程

select会轮询检测所有的连接或者IO,其理论所使用的就是 “事件驱动模型” 这一范式

关于select和poll 的先要更深入的理解,可以参考阅读文章:https://www.jianshu.com/p/397449cadc9a

select模块

select是IO多路复用的一种实现方式,利用select模块来实现非阻塞IO,可通过单线程的方式实现多用户同时访问服务端。
select模块采用水平触发(只要有客户端连接就触发) 来检测server端是否有客户端连入

示例1:


import socket
import select

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(('127.0.0.1',6666))
sk.listen(5)
inp = [sk,]

while True:
    #select 作为监听器(水平触发),来检测是否有客户端来访问
    #每隔3秒执行一次查看,若没有连接 r为空值
    r,w,e = select.select(inp,[],[],3) #(input,output,errorput,time)

    for i in r:
        conn,addr = i.accept()
        print(conn)
        print('hello')
    print('>>>>>>')

当有客户端连接时,打印conn 和 'hello'

47998239.png

示例2:

Server代码:


import socket
import select

sk1 = socket.socket()
sk1.bind(('0.0.0.0',8001))
sk1.listen()

sk2 = socket.socket()
sk2.bind(('0.0.0.0',8002))
sk2.listen()

sk3 = socket.socket()
sk3.bind(('0.0.0.0',8003))
sk3.listen()

inputs = [sk1,sk2,sk3,]

while True:
    r_list,w_list,e_list = select.select(inputs,[],inputs,1)
    for sk in r_list:
        conn,addr = sk.accept()
        conn.sendall('hello'.encode())
        conn.close()

    for sk in e_list:
        inputs.remove(sk)

上面代码表示:select内部会自动监听sk1,sk2,sk3三个对象。监听三个句柄是否发生变化,并将发生变化的元素放入r_list中。若有客户端连接sk2,sk3 则r_list= [sk2,sk3]

client代码:


import socket

obj2 = socket.socket()
obj3 = socket.socket()
obj2.connect(('127.0.0.1', 8002))
obj3.connect(('127.0.0.1', 8003))

content = str(obj2.recv(1024), encoding='utf-8')
print("obj2:"+content)
content = str(obj3.recv(1024), encoding='utf-8')
print("obj3:"+content)

obj2.close()
obj3.close()

客户端发起了两个socket连接,分别对应sk2和sk3

49914684.png

接收返回并打印结果.

49937048.png

参考

Python select模块简单使用

标签: none

添加新评论