非阻塞和IO多路复用

97 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第天,点击查看活动详情

一、非阻塞

socket都是会阻塞的

在等待连接以及等待接收数据的时候进入一个阻塞状态

# 服务端
import socket

phone = socket.socket(socket.AF_INET , socket.SOCK_STREAM)
phone.bind(('127.0.0.1' , 8080))
phone.listen(5)

# 设置非阻塞状态
phone.setblocking(False)

# 保存客户端连接的对象
r_list = []

while True:
    # 捕获阻塞异常
    try:
        # 获取等待客户端连接的对象
        conn , clinent = phone.accept()
        # 把获取到的连接对象进行保存
        r_list.append(conn)
    except BlockingIOError:
        del_list = []
        # 变量对象列表
        for i in r_list:
            try:
                # 接收客户端发送的数据
                data = i.recv(1024)
                print(data.decode('utf-8'))
                i.send(data)
            except BlockingIOError:
                continue
            except ConnectionResetError:
                i.close()
                del_list.append(i)

        for i in del_list:
            r_list.remove(i)
# 客户端
import socket

phone = socket.socket(socket.AF_INET , socket.SOCK_STREAM)

phone.connect(('127.0.0.1' , 8080))

while True:
    msg = input('>>(q退出)').strip()
    if not msg:
        continue
    if msg == 'q':
        break
    phone.send(msg.encode('utf-8'))
    data = phone.recv(1024)
    print(data.decode('utf-8'))

phone.close()

二、IO多路复用

IO多路复用是通过一种机制,可以监视多个文件描述符,一旦有描述符就绪状态(写或者读),就行通知对应的程序进入与之相对应的操作。用于提升效率

r , w , e = select.select(rlist , wlist , errlist)

rlist , wlist , errlist均是文件描述符
rlist :等待读就绪的文件描述 
wlist :等待写就绪的文件描述
errlist :等待异常
import socket
import select

phone = socket.socket(socket.AF_INET , socket.SOCK_STREAM)
phone.bind(('127.0.0.1' , 8080))
phone.listen(5)
# 设置非阻塞状态
phone.setblocking(False)

read_list = [phone]
'''
read_list会存放自己的socket对象 ,对方的客户端连接对象
'''

while True:

    r , w , e = select.select(read_list , [] , [])
    for i in r:
        if i is phone:
            # 获取新的客户端连接对象
            conn , addr = i.accept()
            read_list.append(conn)
        else:
            try:
                # 判断连接对象是否有数据发送
                data = i.recv(1024)
                print(addr)
                print(data.decode('utf-8'))
            except Exception:
                i.close()
                read_list.remove(i)
                continue
            i.send(data)

三、黏包

黏包问题就是:接收端不清楚发送端会发送多长的数据。

解决黏包:让发送端在发送数据之前,先返回一个数据长度 , 在发送数据