• 事件驱动模型与IO模型


    事件驱动模型

     

     

    事件驱动编程,是一种编程范式。这里程序的执行流由外部事件来决定。它的特点是包含一个循环,当外部事件发生时,使用回调机制来触发相应的处理。

    另外2种常见的编程范式是(单线程)同步以及多线程编程

    IO模型

     

     同步IO(synchronous),异步IO(asynchronous),阻塞IO(blocking) 和非阻塞IO(non-blocking) 都是独立的,不一起出现

    5种IO模型如下:

    -blocking IO 

    -nonblocking IO

    -IO multiplexing(IO多路复用)

    -signal driven IO

    -asynchronous IO

    blocking IO (阻塞IO)

    在linux 中,默认情况下所有的socket 都是blocking IO,阻塞IO 只发生了一次系统调用,进程进入阻塞状态时,是不占用CPU的。

     实例:

    -server 端

    import socket

    sk=socket.socket()

    sk.bind(("127.0.0.1",8080))

    sk.listen(5)

    while 1:
    conn,addr=sk.accept()

    while 1:
    conn.send("hello client".encode("utf8"))
    data=conn.recv(1024)
    print(data.decode("utf8"))

    -client端

    import socket

    sk=socket.socket()

    sk.connect(("127.0.0.1",8080))

    while 1:
    data=sk.recv(1024)
    print(data.decode("utf8"))
    sk.send(b"hello server")

    非阻塞IO

    缺点:不能及时收发消息,系统调用太多次

     实例

    -server 端

    import time
    import socket
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sk.bind(('127.0.0.1',6667))
    sk.listen(5)
    sk.setblocking(False) #此代码代表不会一值等待连接(如上图)/非阻塞
    print ('waiting client connection .......')
    while True:
    try:

    connection,address = sk.accept() # 进程主动轮询
    print("+++",address)
    client_messge = connection.recv(1024)
    print(str(client_messge,'utf8'))
    connection.close()
    except Exception as e:
    print (e)
    time.sleep(4)

    -client 端

    import time
    import socket
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    while True:
    sk.connect(('127.0.0.1',6667))
    print("hello")
    sk.sendall(bytes("hello","utf8"))
    time.sleep(2)
    break

    IO multiplexing(IO多路复用)

     

    IO多路复用实现并发的方法:

    1)select  可跨平台使用,但是监听连接数最大为1024

    2)  poll     

    3) epoll (推荐使用):效率高,最好的实现方式

     实例一:

    -server 端

    import socket
    import select
    sk=socket.socket()
    sk.bind(("127.0.0.1",9904))
    sk.listen(5)
    inp=[sk,]
    while True:

    r,w,e=select.select(inp,[],[],5) #[sk,conn] #select监听。
    #inp是指输入内容,第一个[]是输出,第二个[]是报错,数字5是每隔5秒监听一次(可自己设置秒数)
    #此处的r 相当于是谁变动r就等于谁。

    for i in r:#[sk,]
    conn,add=i.accept()
    print(conn)
    print("hello")
    inp.append(conn)
    print('>>>>>>')

    -client 端

    import socket

    sk=socket.socket()

    sk.connect(("127.0.0.1",9904))

    while 1:
    inp=input(">>").strip()
    sk.send(inp.encode("utf8"))
    data=sk.recv(1024)
    print(data.decode("utf8"))

    实例二:

    server 端

    import socket
    import select
    sk=socket.socket()
    sk.bind(("127.0.0.1",8801))
    sk.listen(5)
    inputs=[sk,]
    while True:
    r,w,e=select.select(inputs,[],[],5)#此处的r 相当于是谁变动,r等于谁

    for obj in r:#[sk,]
    if obj==sk:
    conn,add=obj.accept()#此处相当于取出sk,已监听sk.
    print(conn)
    inputs.append(conn)
    else:
    data_byte=obj.recv(1024)
    print(str(data_byte,'utf8'))
    inp=input('回答%s号客户>>>'%inputs.index(obj))
    obj.sendall(bytes(inp,'utf8'))

    print('>>',r)

    当客户端与服务端连接上的时候 :inputs= [sk,] select 可以捕捉到sk变动,监听sk,执行obj ==sk 条件下代码,将conn 加入到inputs中,所以此时inputs = [sk,conn,] 此时监听sk和 conn.

    但是当发消息时,因为只有conn变动,所以运行的是else下代码

    client 端

    import socket

    sk = socket.socket()
    sk.connect(('127.0.0.1', 8801))

    while True:
    inp = input(">>>>")
    sk.sendall(bytes(inp, "utf8"))
    data = sk.recv(1024)
    print(str(data, 'utf8'))

    python 封装的实现IO多复路模型的模块-selectors

     

     

    服务端代码如下:

    import selectors
    import socket
    sel = selectors.DefaultSelector() #创建selector 对象

    def accept(sock,mask):
    conn,addr = sock.accept()
    print('accepted', conn, 'from', addr)
    conn.setblocking(False)#设置非阻塞,代码代表不会一直等待
    sel.register(conn,selectors.EVENT_READ,read)
    #将conn 与read函数绑在一起,并将conn 加入sel.select()列表中

    def read(conn,mask):
    try:
    data = conn.recv(1000)
    if not data:
    raise Exception
    print('echoing', data.decode('utf-8'), 'to', conn)
    conn.send(data)
    except Exception as e:#一旦断开连接,会执行以下代码
    print('closing', conn)
    sel.unregister(conn)
    #此代码代表解绑conn,意味着sel.select()列表中不在有它
    conn.close()

    sock = socket.socket()
    sock.bind(("192.168.1.5",8082))
    sock.listen(1000)
    sock.setblocking(False)#设置socket 非阻塞,代码代表不会一直等待
    sel.register(sock, selectors.EVENT_READ, accept)
    #将socket 与accept函数绑在一起,并将sock 加入sel.select()列表中
    print("server.....")
    #当与客户端连接上时,sock有变化,events = [sock,],callback =sock=accept,accept(),accept执行,代码如下。
    此时已经监听了活动socket,而accept()函数执行又将conn加入events中进行监听。
    #当发送消息时,conn 有变化,所以此时的events = [conn,],callback = conn =read,read()执行,代码同样如下。

    while True:
    events =sel.select()#此处events相当于是变动的监听对象,谁变动,此处就是谁
    for key, mask in events:
    callback = key.data
    #当与客户端连接上时,第一次运行时此处相当于callback=accept
          #发送消息时,callback = read
            callback(key.fileobj, mask)
    #第一次与客户端连接时,此处相当于accept(sock,mask),运行accept 函数。
         #发消息时,此处=read(conn,mask)

    客户端

    import socket

    sk=socket.socket()

    sk.connect(("192.168.1.5",8082))
    while 1:
    inp = input(">>>>").strip()
    sk.send(inp.encode('utf-8'))
    data = sk.recv(1024)
    print(data.decode('utf-8'))

     

     

    异步IO(asynchronous IO)

    异步IO:全程无阻塞

     以下是这几种IO模型的区别图:

     

    作业:用selectors 实现文件的上传与下载并发。 

     客户端:


    import socket
    import sys,os
    Base_DIR = os.path.basename(os.path.abspath(__file__))

    class selectFtClient:
    def __init__(self):
    self.args = sys.argv # sys.argv 相当于系统从外部获取数据,以列表形式存放[代码本身文件路径,输入内容]
    if len(self.args) > 1:
    self.port = (self.args[1], int(self.args[2]))
    else:
    self.port = ('192.168.1.4', 8080)
    self.creat_socket()
    self.command_fanout()

    def creat_socket(self):
    try:
    self.sk = socket.socket()
    self.sk.connect(self.port)
    print('连接服务器成功')
    except Exception as e:
    print("error",e)

    def command_fanout(self):
    while True:
    cmd = input('>>>').strip()
    if not cmd: #代表输入为空
    break
    cmd,file = cmd.split()
    if hasattr(self,cmd):#判断是否有cmd方法
    func = getattr(self,cmd)
    func(cmd,file)
    else:
    print('调用错误!')
    def put(self,cmd,file):
    if os.path.isfile(file):
    fileName = os.path.basename(file)
    fileSize = os.path.getsize(file)
    fileInfo = "%s|%s|%s"%(cmd,fileName,fileSize)
    self.sk.send(fileInfo.encode('utf8'))
    recvStatus = self.sk.recv(1024)
    print('recvStatus',recvStatus)
    hasSend = 0
    if recvStatus.decode('utf8')=="ok":
    with open(file,'rb') as f:
    while fileSize > hasSend:
    contant = f.read(1024)
    recv_size = len(contant)
    self.sk.send(contant)
    hasSend +=recv_size
    s= str(int(hasSend/fileSize*100))+"%"
    print('正在上传文件:%s,已上传:%s'%(fileName,s))
    print('%s上传完毕'%fileName)
    else:
    print('文件不存在')

    def get(self,cmd,file):
    pass


    if __name__ == "__main__":
    selectFtClient()

    服务端:

    import os
    import sys
    import time
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))#upload项
    import socket
    import selectors


    class selectFtpServer:
    def __init__(self):
    self.dic = {}
    self.sel = selectors.DefaultSelector() #创建selectors对象
    self.creat_socket()
    self.handle()


    def creat_socket(self):
    server = socket.socket()#创建socket
    server.bind(('192.168.1.4',8080))#绑定端口
    server.listen(5)#监听
    server.setblocking(False)#设置不阻塞
    self.sel.register(server,selectors.EVENT_READ,self.accept)
    print('服务端已开启,等待用户连接....')

    def handle(self):
    while True:
    events = self.sel.select()#监听有变化的
    for key,mask in events:
    callback =key.data
    callback(key.fileobj,mask)

    def accept(self,sock,mask):
    conn,addr = sock.accept
    print('connected conn: %s '%conn)
    print('connected addr: %s '%addr)
    conn.setblocking(False)
    self.sel.register(conn,selectors.EVENT_READ,self.read)
    self.dic[conn]={}
    def read(self,conn,mask):
    if not self.dic[conn]:
    data = conn.recv(1024)
    cmd, filename, filesize = data.decode('utf8').split('|')
    self.dic = {conn: {'cmd': cmd, 'filename': filename, 'filesize': int(filesize)}}

    if cmd == 'put':
    conn.send("ok".encode('utf8'))
    if self.dic[conn]['cmd'] == 'get':
    file = os.path.join(BASE_DIR, 'download', filename)
    if os.path.exists(file):
    fileSize = os.path.getsize(file)
    send_info = "%s|%s" % ('yes',fileSize)
    conn.send(send_info.encode('utf8'))
    else:
    send_info = '%s|%s'%('no',0)
    conn.send(send_info.encode('utf8'))
    else:
    if self.dic[conn].get('cmd', None):
    cmd = self.dic[conn].get('cmd')
    if hasattr(self, cmd):
    func = getattr(self, cmd)
    func(conn)
    else:
    print('error cmd !')
    conn.close()
    else:
    print('error cmd !')


    def put(self,conn):

    fileName = self.dic[conn]['fileName']
    fileSize = self.dic[conn]['fileSize']
    path = os.path.join(BASE_DIR,'upload',fileName)
    recv_data = conn.recv(1024)
    self.hasRecv +=len(recv_data)

    with open(path,'ab') as f:
    f.write(recv_data)
    if fileSize ==self.hasRecv:
    if conn in self.dic.keys():
    self.dic[conn]={}
    print('%s上传完毕!'%fileName)


    if __name__ == "__main__":
    selectFtpServer()

     

  • 相关阅读:
    前端性能优化
    web缓存
    js实现数组去重并且显示重复的元素和索引值
    前端面试简单整理
    js记录重复字母的个数
    js数组快速排序
    sql数据库链接
    w3cschool -css
    w3cschool -html
    A*算法在栅格地图上的路径搜索(python实现)
  • 原文地址:https://www.cnblogs.com/wode110/p/15006654.html
Copyright © 2020-2023  润新知