同步阻塞io
- io就是input,output,即输入和输出,读和写
接收数据为input,发送数据为output
读文件为input,写文件为output
- 同步阻塞:用户发起read请求,系统内核会去获取数据,在数据到达前,你的read线程会一直等在这个地方,不能做任何事情,直到数据到达,然后拷贝进用户缓冲区,read才完成,才继续往下执行
- 默认的套接字就是同步阻塞的
同步非阻塞io
1. 用户发起read请求,如果内核的数据没有到达,立刻返回,然后不断循环的去发起read请求,直到有数据就读取数据
用户线程每次请求io都可以立刻返回,但是为了拿到数据,需要不断轮询,无谓的消耗了大量的CPU
一般很少直接使用这种模型,但是在其他io模型中使用非阻塞io这一特性
2. 非阻塞套接字
1. 设置非阻塞
setblocking(False)
2. accept,recv
阻塞方法不阻塞,没有连接,没有数据会引发blockingioerror,所以需要用异常来捕获
3. 同步非阻塞socket实现并发
4. io多路复用
非阻塞socket实现并发
1.思路:
外循环用来不断的accept,接收连接,用一个列表来保存所有建立的对等socket,并设置为非阻塞
内循环用来循环这个保存对等socket是否有数据传入
# 非阻塞套接字实现并发
import socket
server = socket.socket()
server.setblocking(False) #设置非阻塞
server.bind(("",6969))
server.listen(10)
client_list = [] #存放连接进来的客户端
#设置一个大循环,不断地接收用户的请求
while True:
#接收请求
try:
conn,addr = server.accept() #没有连接他会引发blockingioerror异常
conn.setblocking(False) # 设置这个对等连接为非阻塞
client_list.append((conn,addr))
print("连接:{}".format(addr))
except BlockingIOError:
pass
#处理请求
for client,addr in client_list:
try:
recv_data = client.recv(1024) #收数据
if recv_data:
print("接收来自:{}>>>{}".format(
addr,recv_data.decode("utf-8")
))
client.send(recv_data) #发送数据
else:
client_list.remove((client,addr)) #从列表中删除
client.close()
print("断开连接:{}".format(addr))
except BlockingIOError:
pass
io多路复用
- epoll是inux系统下最快的技术,大部分服务器都采用epoll
- windows下没有epoll,Windows有select
- 是事件驱动io
使用步骤:
1. 创建epoll
2. 创建回调函数
3. 注册socket
4. 循环监听
# 通过epoll实现io多路复用实现并发的服务器
import socket
import selectors
server = socket.socket()
#设置非阻塞和阻塞无所谓
server.bind(("",6969))
server.listen(1000)
def recv(soc):
data = soc.recv(1024)
if data:
soc.send(data)
else:
#删除注册
epoll_selector.unregister(soc)
soc.close()
def accept(soc):
#接收请求
conn,addr = soc.accept()
#把对等连接注册到epoll监视器
epoll_selector.register(
conn,
selectors.EVENT_READ,
recv
)
#创建一个epoll监视器
epoll_selector = selectors.DefaultSelector() # 根据操作系统自行选择
#注册服务端server
epoll_selector.register(
server, #主服务的socket
selectors.EVENT_READ, #事件类型
accept #回调函数
)
#循环监听
while True:
events = epoll_selector.select() #epoll_select是阻塞的
# events就是所有的被触发了的事件
for key,mark in events:
sock = key.fileobj #socket对象
callback = key.data
#执行回调函数
callback(sock)
多人聊天客户端
import socket
server = socket.socket()
server.setblocking(False) #设置非阻塞
server.bind("",6969)
server.listen(5)
clients = [ ] 存放连接进来的客户端
while True:
#循环接收客户端
try:
conn,addr = server.accept()
conn.setblocking(False) #设置这个对等连接为非阻塞
clients.append((conn,addr))
except Exception:
pass
#循环处理对等连接
for client,addr in clients:
#收数据
try:
recv_data = client.recv(1024)
if recv_data:
#给所有的人发一份
data = “receive {} from {}”.format(
recv_data.decode(“utf-8”),
addr
)
for c,a in clients:
c.send(recv_data)
else:
clients.remove((client,addr))
client.close()
except Exception:
pass
#发送端
import socket
client = socket.socket()
client.connect((“127.0.0.1”,6969))
while True:
data = input(">>>")
if data = “q”:
break
client.send(dta.encode)
client.close()
#接收端
import socket
client = socket.socket()
client.connect((“127.0.0.1”,6969))
while True:
res = client.recv(1024)
print(res.decode(“utf-8”))