python 第二十二章 非堵塞& IO多路复用

71 阅读3分钟

大家好我是程序员_蓝天, 本文是为了方便初学者能够快速学习python,写的可能不是很好,希望大家多多包涵。 每个赞都是我前进的动力

一,非堵塞

socket 默认是堵塞的状态

在等待连接以及等待接收数据的时候都会进入堵塞的状态

# 套接字对象.setblocking(False)  设置套接字为非堵塞状态
import socket

# 1. 创建服务器对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2 绑定ip 以及port 
server.bind(('127.0.0.1',20000))

# server.setblocking(False)  设置套接字为非堵塞状态
server.setblocking(False)

# 监听客户端 
server.listen(5)
# r_li 存放客户端连接对象
r_li = []

while 1:
    try:
        # server.accept() 等待客户端连接
        conn, addr = server.accept()
        r_li.append((conn, addr))
        
    # 捕捉异常 无客户端连接时 
    except BlockingIOError:
        # del_li 存放 已关闭 客户端连接对象
        del_li = []  
        for item in r_li:
            try:
                # 客户端连接对象, ip,port = (客户端连接对象, ip,port)
                conn, addr = item

                # conn.recv(1024) 接收客户端发送的数据
                data = conn.recv(1024)

                if not data:
                    conn.close()
                    del_li.append(item)
                    
                print(f'{data.decode("utf-8")}-------{addr}')
                conn.send(data)

            except BlockingIOError:
                continue
            except ConnectionResetError:
                conn.close()
                del_li.append(item)
        
        
        # 从r_li 移除已关闭的对象
        for i in del_li:
            r_li.remove(i)


客户端

# 客户端

import socket

# 1. 创建客户端对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器 ip&端口
client.connect(('127.0.0.1', 20000))

while 1:
    # 获取用户数据
    msg = input(">>>>(q:退出)").strip()
    # 判断是否为空
    if not msg:
        continue
    if msg == 'q':
        break

    client.send(msg.encode('utf-8'))
    server_data = client.recv(1024).decode('utf-8')
    print(f'服务端发送数据{server_data}')

client.close()

二,IO多路复用

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

r,w,e = select.select(rlist,wlist, errlist)
rlist,wlist,errlist 都是文件描述符
rlist 指的就是等待读就绪的文件描述
wlist 指的就是等待写就绪的文件描述
errlist 等待异常
import socket
import select
# 1. 创建服务器对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2 绑定ip 以及port
server.bind(('127.0.0.1',20001))

# server.setblocking(False)  设置套接字为非堵塞状态
server.setblocking(False)

# 监听客户端
server.listen(5)

# r_li 存放客户端连接对象, 存放自己
read_list = [server]

while 1:
    r,w,e = select.select(read_list,[],[])
    for i in r:
        # 1. 判断是否为服务器对象
        if i is server:
            # 获取新的客户端连接对象
            conn, addr = i.accept()
            # 把新的客户端连接对象 添加的read_list
            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)

三,黏包

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

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

服务端

# 黏包
import socket
import struct
# 1. 创建服务器对象
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2 绑定ip 以及port
server.bind(('127.0.0.1',20001))

server.listen(5)

conn, addr = server.accept()

while 1:
    try:
        data = conn.recv(1024)

        data01 = b'ab'
        data02 = b'bd'

        head_len = struct.pack('i', len(data01 + data02))
        print(head_len, "数据长度")
        conn.send(head_len)  # 发送数据长度
        conn.send(data01 + data02)  # 发送数据

    except Exception as e:
        conn.close()
        break

server.close()

客户端

# 客户端

import socket
import struct
# 1. 创建客户端对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. 连接服务器 ip&端口
client.connect(('127.0.0.1', 20001))

while 1:
    # 获取用户数据
    msg = input(">>>>(q:退出)").strip()
    # 判断是否为空
    if not msg:
        continue
    if msg == 'q':
        break

    client.send(msg.encode('utf-8'))
    # --------接收服务器数据
    server_head = client.recv(1024)
    server_data = struct.unpack("i", server_head)
    print(server_data)
    print('报文长度',server_data[0])
    rest = b''
    rest_len = 0
    while rest_len < server_data[0]:
        re_data = client.recv(1024)
        rest += re_data
        rest_len += len(re_data)

    print(rest.decode('utf-8'))



client.close()