一. 基于poll方法的IO多路复用
-
p = select.poll()
功能 : 创建poll对象
返回值: poll对象 -
p.register(fd,event)
功能: 注册关注的IO
参数: fd 要关注的IO的fileno(整数)
event 关注IO的事件类型常用事件 : POLLIN 读IO (rlist) POLLOUT 写IO (wlist) POLLERR 异常IO (xlist) POLLHUP 断开连接 e.g. p.register(sockfd, POLLIN|POLLERR)
-
p.unregister(fd)
功能: 取消对IO的关注
参数:IO对象或者IO对象的fileno -
events = p.poll()
功能:阻塞等待监控的IO事件发生
返回值:
events 格式:[(fileno,event),(),…]需要通过fileno寻找对应的IO对象,以操作IO事件。建立字典作为查找地图:{fileno:io_obj}
poll服务端
from select import *
from socket import *
# 创建关注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(("0.0.0.0",9999))
s.listen(5)
# 创建poll对象
p = poll()
# 建立地图
fdmap = {s.fileno():s}
# 关注IO
p.register(s,POLLIN | POLLERR)
# 循环监控IO
while True:
events = p.poll()
# 遍历events 处理IO
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("shodao",addr)
# 添加新的关注IO
p.register(c,POLLIN | POLLHUP)
fdmap[c.fileno()] = c
elif event & POLLHUP:
print("客户端退出")
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
elif event & POLLIN:
data = fdmap[fd].recv(1024)
print(data.decode())
fdmap[fd].send(b"OK")
二. epoll 实现IO多路复用
-
使用方法: 基本与poll相同
生成对象改为epoll()
将所有事件类型改为EPOLL事件 -
epoll特点 :
* epoll 效率比select poll要高 * epoll 可以同时监控的IO数量比select poll多 * epoll 的触发方式更多 (EPOLLET边缘触发)
epol的服务端
from select import *
from socket import *
# 创建关注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#当参数为SO_REUSERADDR时为端口从用,
s.bind(("0.0.0.0",9999))#绑定套接字
s.listen(5)#设置监听套接字
# 创建epoll对象
p = epoll()
# 建立地图
fdmap = {s.fileno():s}
# 关注IO
p.register(s,EPOLLIN | EPOLLERR)
# 循环监控IO
while True:
events = p.poll()
# 遍历events 处理IO
for fd,event in events:
if fd == s.fileno():
c,addr = fdmap[fd].accept()
print("shodao",addr)
# 添加新的关注IO
p.register(c,EPOLLIN | EPOLLHUP)
fdmap[c.fileno()] = c
elif event & EPOLLHUP:
print("客户端退出")
p.unregister(fd)
fdmap[fd].close()
del fdmap[fd]
elif event & EPOLLIN:
data = fdmap[fd].recv(1024)
print(data.decode())
fdmap[fd].send(b"OK")
三. struct 模块使用
-
原理 : 将一组简单数据进行打包,转换为bytes格式发送,或者将一组bytes个数数据转换为python数据类型
-
接口使用
【1】st = Struct(fmt) 功能: 生成结构化对象 参数: fmt 定制的数据结构 e.g. 要组织的数据 1 b'lisi' 1.75 fmt : 'i4sf' 【2】 st.pack(v1,...) 功能: 将一组数据按照指定格式打包转换 参数: 要打包的数据 返回值: bytes字节串 【3】 st.unpack(bytes_data) 功能: 将bytes字节串按照格式解析 参数: bytes字节串 返回值: 解析后的数据元组 【4】 struct.pack(fmt,v1,...) struct.unpack(fmt,bytes_data) 说明 : 可以使用struct模块直接调用pack,unpack,第一个参数直接传入fmt
练习 : 从客户端输入学生的id 姓名 年龄 成绩,打包发送给服务端,服务端将其存入一个数据表中。
struct发送端
from socket import *
import struct
fmt = "i32sif"
ADDR = ("127.0.0.1",9999)
s = socket(AF_INET,SOCK_DGRAM)
while True:
print("\n*************")
id = int(input("ID:"))
name = input("姓名:")
age = int(input("年龄:"))
score = float(input("分数:"))
data = struct.pack(fmt,id,name.encode(),age,score)
s.sendto(data,ADDR)
s.close()
struct接收端
from socket import *
import struct
import pymysql
# 创建数据库游标对象
db = pymysql.connect("localhost","root","123456","bank")
cursor = db.cursor()
s = socket(AF_INET,SOCK_DGRAM)
s.bind(("0.0.0.0",9999))
# 确定数据格式
st = struct.Struct("i32sif")
while True:
data ,addr = s.recvfrom(1024)
# 解析
data = st.unpack(data)
id = data[0]
name = data[1].decode()
age = data[2]
score = data[3]
sql = "insert into student VALUES(%d,'%s',%d,%f)"%(id,name,age,score)
try:
cursor.execute(sql)
db.commit()
except Exception as e:
db.rollback()
print(e)
s.close()
db.close()
四. 本地套接字
-
功能 : 本地两个程序之间的数据交换
-
通信原理:对一个内存对象进行读写操作,完成两个程序间的数据交互
-
创建本地套接字
【1】 创建本地套接字: sockfd = socket(AF_UNIX,SOCK_STREAM) 【2】 绑定套接字文件 sockfd.bind(file) 【3】 监听,连接,收发消息 listen accept recv/send
本地套接字发送端
from socket import *
# 两边使用同一个套接字文件
sock_file = "./sock"
sockfd = socket(AF_UNIX,SOCK_STREAM)
sockfd.connect(sock_file)
while True:
msg = input(">>>>>>>>:")
if not msg:
break
sockfd.send(msg.encode())
sockfd.close()
本地套接字接收端
from socket import *
import os
# 本地套接字文件
sock_file = "./sock"
# 判断文件是否存在
if os.path.exists(sock_file):
os.remove(sock_file)
# 创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
# 绑定文件
sockfd.bind(sock_file)
sockfd.listen(3)
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if not data:
break
print(data.decode())
c.close()
sockfd.close()