python之模拟io模式

python之模拟io模式,第1张

概述环境:python3.8参考:  https://segmentfault.com/a/1190000003063859(linux io模式等,本文的原理图片来自这个)    https://blog.csdn.net/zhuangzi123456/article/details/84400108(select的使用)    https://blog.51cto.com/linzb/1911468(以前的笔记)本文所有模型的客户端

环境:python3.8

参考:

    https://segmentfault.com/a/1190000003063859(linux io模式等,本文的原理图片来自这个 )

    https://blog.csdn.net/zhuangzi123456/article/details/84400108(select的使用)

    https://blog.51cto.com/linzb/1911468(以前的笔记)

本文所有模型的客户端程序如下:

import  socketc=socket.socket()c.connect(("127.0.0.1",456))while True:    c.sendall(input(":").encode('utf-8'))    print(c.recv(1024).decode('utf-8'))

民以食为天,让我们从吃饭说起。

假设你去食堂吃饭(取用户空间外的数据),你要做的事是什么?

排队取餐(发起系统调用)

  2.打饭阿姨等厨师的饭菜端上来(内核等数据)

  3.油腻的抽风阿姨把饭打给你(内核拷贝数据)

这就是所谓的阻塞I/O模型啦

体现在代码就跟简单的socket程序一样

from socket import *backlog=10buffsize=1024server=socket(AF_INET,SOCK_STREAM)server.bind(("127.0.0.1",5666))server.Listen(backlog)clIEntsocket, clIEntaddr = server.accept()while True:    msg=clIEntsocket.recv(buffsize)    print('clIEnt msg:', msg.decode('utf-8'))    clIEntsocket.sendall(input('your msg:').encode('utf-8'))

recv方法发起了一个系统调用,等待内核把数据拷贝到用户内存区,然后用户进程才能读取数据。

为什么要这么拷贝数据呢?

因为socket程序运行在用户态,而接受的数据被客户端发送到网卡,网卡是硬件,只有内核才有资格去 *** 作硬件

这种模型会阻塞在两个地方:

用户在等待用户空间有数据(你在等阿姨把饭给你)

系统在等待取得数据(阿姨在等厨师做好饭)

这好比饭还有很久才熟,你却只能在那等着,不能出去玩,于是你想了个好办法:

你还是出去玩,但是隔一段时间回来问下:我的饭熟了吗?

这个就是所谓的不阻塞io

这里我利用socket模块的setblocking来模拟这个过程,当服务器收到客户端的链接的时会打印“hello”,其他时候隔3秒打印“waiting”,代码如下:

import socket,select,times=socket.socket()s.bind(("127.0.0.1",456))s.Listen(5)s.setblocking(0)while 1:    try:        print ('waiting')        s.accept()        print('hello')    except Exception:        time.sleep(3)

显然,这种模型要自己大量的来回询问,所以你又想了个偷懒的办法:

色诱打饭阿姨,给阿姨留个号码,让她饭好了告诉你声,自己跑出去玩

这就是所谓的io复用:

给阿姨留号码又分两种:

    一种是匿名电话,每次阿姨都要询问这是哪个英俊的小伙子的xx餐好了[,收匿名电话的阿姨也分两类,有原则的(一次1024个,用select实现),无原则的(不限个数,用poll实现)]

    一种是留姓名的,阿姨省去轮询步骤,饭好了直接通知(用epoll实现),可以想象,这种效率是最高的

select实现如下:参考:https://blog.csdn.net/zhuangzi123456/article/details/84400108

import socket,select,times=socket.socket()s.bind(("127.0.0.1",456))s.Listen(5)s.setblocking(0)l=[s,]while True:    r,w,err=select.select(l,[],[])    for i in r:        if s==i:            conn,addr=s.accept()            conn.setblocking(0)            l.append(conn)        else:            print(i.recv(1024).decode('utf-8'))            i.sendall('hello sir'.encode('utf-8'))

deBUG:

    select监听的三个列表不能初始内容都为空,否则后续会报错(具体原因待查明)

epoll实现如下:(抄自https://my.oschina.net/SnifferApache/blog/776123)

import socket, selects=socket.socket()host="127.0.0.1"port=8889s.bind((host,port))fdmap={s.fileno():s}s.Listen(5)p=select.poll()p.register(s)while	True:	events=p.poll()	for fd,event in events:		if fd==s.fileno():			c,addr = s.accept()			print ('got connection from ',addr)			p.register(c)			fdmap[c.fileno()] = c		elif event & select.PolliN:			data=fdmap[fd].recv(1024)			if not data:				print (fdmap[fd].getpeername(),'disconnected')				p.unregister(fd)				del fdmap[fd]			else:	print (data)

epoll实现如下:(抄自http://scotdoyle.com/python-epoll-howto.html)

import socket, selectEol1 = b'\n\n'Eol2 = b'\n\r\n'response = b'http/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'response += b'Hello, world!'serversocket = socket.socket(socket.AF_INET, socket.soCK_STREAM)serversocket.setsockopt(socket.soL_SOCKET, socket.so_REUSEADDR, 1)serversocket.bind(('0.0.0.0', 8080))serversocket.Listen(1)serversocket.setblocking(0)epoll = select.epoll()epoll.register(serversocket.fileno(), select.EPolliN)try:   connections = {}; requests = {}; responses = {}   while True:      events = epoll.poll(1)      for fileno, event in events:         if fileno == serversocket.fileno():            connection, address = serversocket.accept()            connection.setblocking(0)            epoll.register(connection.fileno(), select.EPolliN)            connections[connection.fileno()] = connection            requests[connection.fileno()] = b''            responses[connection.fileno()] = response         elif event & select.EPolliN:            requests[fileno] += connections[fileno].recv(1024)            if Eol1 in requests[fileno] or Eol2 in requests[fileno]:               epoll.modify(fileno, select.EPolLOUT)               print('-'*40 + '\n' + requests[fileno].decode()[:-2])         elif event & select.EPolLOUT:            byteswritten = connections[fileno].send(responses[fileno])            responses[fileno] = responses[fileno][byteswritten:]            if len(responses[fileno]) == 0:               epoll.modify(fileno, 0)               connections[fileno].shutdown(socket.SHUT_RDWR)         elif event & select.EPolLHUP:            epoll.unregister(fileno)            connections[fileno].close()            del connections[fileno]finally:   epoll.unregister(serversocket.fileno())   epoll.close()   serversocket.close()

现在好了,你已经能算好点过来等饭了,但是打饭还是需要时间呀,于是你:

献身阿姨,让阿姨在你来的时候就已经打好饭等你,你只需要取走,这就是所谓的异步io模型

显然这种模型是最为高效的,这里我能力有限不提供代码,最后推荐看:https://segmentfault.com/a/1190000003063859

总结

以上是内存溢出为你收集整理的python之模拟io模式全部内容,希望文章能够帮你解决python之模拟io模式所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1188156.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存