IO在网络方面的处理及应用

  • IO
在内存中存在数据交换的操作都可以认为是IO操作
和终端交互:input print
和磁盘交互:read  write
和网络交互:recv  send
  • IO密集型程序
    在程序执行过程中存在大量IO操作,而CPU运算操作较少
    消耗cpu较少,运行效率较低
  • 计算(cpu)密集型程序
    在程序执行中cpu运算较多,IO操作相对较少
    消耗cpu大,运行速度快
  • IO分类
    阻塞IO 非阻塞IO IO多路复用
阻塞IO(类似于从头进行到尾)
	是IO的默认形态,是效率较低的一种IO情形。
	阻塞情况:
		1、因为某种情况没有达成造成的阻塞
			eg:accept input recv
		2、处理IO数据传输时间较长形成的阻塞
			eg:网络传输过程 文件读写过程

非阻塞IO
	通过修改IO事件的属性,使其变成非阻塞状态。(让一些条件阻塞
	函数不再阻塞)
	阻塞情况:
		1、非阻塞IO一般和循环判断一起执行
		2、超时检测:将原本阻塞的函数设置一个最长阻塞时间,如果
		时间内条件达成则正常进行,如果仍然阻塞则视为超时,继续
		向下运行或产生异常	
	操作:
		s.setblocking(False)
		s.settimeout(sec)
  • 普通非阻塞IO
#此程序用来设置非阻塞IO,并在阻塞时每隔两秒打印当前时间
from socket import *
from time import sleep,ctime

s = socket()
s.bind(('0.0.0.0',8888))

s.listen(3)

#将套接字设置为非阻塞
s.setblocking(False)

while True:
	print('Waiting for connect...')
	try:
		c,addr = s.accept()
	#处理非阻塞异常:每隔两秒打印当前时间
	except BlockingIOError:
		sleep(2)
		print(ctime())
		continue
	else:
		print("Connect from",addr)
		while True:
			data = c.recv(1024).decode()
			if not data: 
				break
			print(data)
			c.send(ctime().encode())
		c.close()
  • 设置超时时间的非阻塞IO
#此程序用来设置非阻塞IO,并设置5秒超时时间
#在阻塞时每隔两秒打印当前时间
from socket import *
from time import sleep,ctime

s = socket()
s.bind(('127.0.0.1',8888))

s.listen(3)

#将套接字设置为非阻塞
s.setblocking(False)

#给套接字设置超时时间
#超时会发成timeout异常
s.settimeout(5)

while True:
	print('Waiting for connect...')
	try:
		c,addr = s.accept()
	
	#处理非阻塞异常:每等待5秒超时时间后执行其他代码或产生异常
	#此处为每两秒打印当前时间
	except timeout:
		sleep(2)
		print(ctime())
		continue
	else:
		print("Connect from",addr)
		while True:
			data = c.recv(1024).decode()
			if not data: 
				break
			print(data)
			c.send(ctime().encode())
		c.close()
  • IO多路复用
    定义:同时监控多个IO事件,当哪个IO事件准备就绪就执行哪个IO事件。以此形成可用同时操作多个IO的并发行为,避免一个IO阻塞,造成所有IO都无法执行。
  • IO准备就绪:是一种IO必然要发生的临界条件
IO多路复用的编程实现
	1、将IO设置为关注ID
	2、将关注IO提交给内核检测
	3、处理内核给我们反馈的准备就绪的IO	
	具体方案:
	select --》windows linux unix
	poll   --》linux unix
	epoll  --》linux unix
  • select编程实现
import select
es,ws,xs = select.select(rlist, wlist, xlist[, timeout])
	 -> (rlist, wlist, xlist)
功能:监控IO事件,阻塞等待IO事件发生
参数:rlist	列表:存放我们监控等待处理的IO事件
	  wlist	列表:存放我们要主动操作的IO事件
	  xlist	列表:我们要关注出错处理的IO事件
	  timeout	超时时间
返回值:
	rs	列表	rlist中准备就绪的IO
	ws	列表	wlist中准备就绪的IO
	xs	列表	xlist中准备就绪的IO

注意:
	1、wlist中如果有IO事件则select立即会返回给ws
	2、在处理IO过程中不要出现一个客户端长期占有服务端
	使服务端无法运行到select的情况
	3、IO多路复用占用计算机资源少,IO效率高
  • select实例代码
#此程序演示select处理IO多路复用,可以连接多个客户端
from select import select
from socket import *

s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

rlist = [s]
wlist = []
xlist = []

#循环检测并处理三个列表中的IO事件
while True:
	#监听是否有IO事件发生
	rs,ws,xs = select(rlist,wlist,xlist)
	for r in rs:
		if r is s:
			c,addr = s.accept()
			print("Connecting from:",addr)
			rs.append(c)
		else:
			data = r.recv(1024).decode()
			if not data:
				rs.remove(r)
				r.close()
			else:
				print("receive the info:",data)
				#将客户端套接字放入wlist关注列表
				wlist.append(r)
				

	for w in rs:
		'''
		wlist中的元素相当于向服务端发送信息的客户端
		'''
		send_msg = 'receive yout info'
		w.send(send_msg.encode())
		wlist.remove(w)

	for x in xs:
		pass
  • poll编程实现
1、创建poll对象
	p = select.poll()
2、添加注册事件
	p.register(s,POLLIN | POLLERR) 
	无第二个参数表示全部跟踪
	POLLIN POLLOUT POLLERR POLLHUP POLLNVAL
	rlist   wlist    xlist   断开   无效数据
	
	p.unregister(s)
	从关注事件中移出
3、阻塞等待IO发生
	event = p.poll()
	功能:阻塞等待IO发生
	返回值:events是一个列表,列表中给每一个元素都是一个元
	组,代表一个发生的IO事件
	[(fileno,            event),(),()...]
	就绪IO的文件描述符    具体就绪事件

 * 需要通过文件描述符(fileno)找到IO对象
{s.fileno() : s}
4、处理具体的IO
  • poll编程实践
#此程序用来演示poll进行IO多路复用
from socket import *
from select import *

#创建套接字作为我们关注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',8888))
s.listen(5)

#创建poll对象
p = poll()

#注册关注的IO,监控POLLIN和POLLERR功能
p.register(s,POLLIN | POLLERR)

#通过fileno找到IO对象的字典
fdmap = {s.fileno():s}


while True:
	#进行IO监控
	events = p.poll()
	#返回值为[(fileno,event),(),(),()...]
	for fd,event in events:
		if fd == s.fileno():
			c,addr = fdmap[fd].accept()
			print('Connect from',addr)
			#添加新的关注事件
			
			p.register(c,POLLIN | POLLHUP)
			fdmap[c.fileno()] = c

		elif event & POLLIN:
		#监测到要接收数据的对象
			data = fdmap[fd].recv(1024)
			if not data:
				#从关注事件移除
				p.unregister(fd)
				fdmap[fd].close()
				del fdmap[fd]
			else:
				print(data.decode())
				fdmap[fd].send(b'Receive')
  • epoll方法
    因为python内部的分装,epoll与poll使用方法几乎相同
    使用方法:
    *将构造函数select.poll()改为select.epoll()
    *将所有poll对象属性改为epoll对象属性
    区别:
    epoll的效率要比polll和select高
    epoll的事件触发方式更多
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值