IO多路复用--poll/epoll、struct模块。本地套接字

一. 基于poll方法的IO多路复用

  1. p = select.poll()
    功能 : 创建poll对象
    返回值: poll对象

  2. 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)
    
  3. p.unregister(fd)
    功能: 取消对IO的关注
    参数:IO对象或者IO对象的fileno

  4. 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 模块使用

  1. 原理 : 将一组简单数据进行打包,转换为bytes格式发送,或者将一组bytes个数数据转换为python数据类型

  2. 接口使用

     【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. 功能 : 本地两个程序之间的数据交换

  2. 通信原理:对一个内存对象进行读写操作,完成两个程序间的数据交互

  3. 创建本地套接字

      【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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值