大家好我是程序员_蓝天, 本文是为了方便初学者能够快速学习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()