普通套接字的缺陷
普通套接字一次只能服务一个客户端,而且若无客户端连接,accept()一直阻塞,无数据传输,recv一直阻塞,传输效率极低。
普通服务器的IO模型
操作系统监听用户进程的状态,若无数据,则一直等待,知道用户请求数据,操作系统才进行数据处理。
普通套接字实现的服务端的瓶颈
- 在没有新的套接字来之前, 不能处理已经建立连接的套接字的请求(多个客户端连接只有一个被服务)。
- 在没有接受到客户端请求数据之前, 不能与其他客户端建立连接!
非阻塞套接字与普通套接字的区别
In [34]: import socket
In [35]: socket_server = socket.socket()
In [36]: socket_server.setblocking(False) #将scoket设置为非阻塞 注意:必须在bind和listen之
前!
In [37]: socket_server.bind(('', 8087))
In [38]: socket_server.listen(5)
In [39]: socket_server.accept() #没有连接就会引发BlockingIOError异常
---------------------------------------------------------------------------
BlockingIOError Traceback (most recent call last)
<ipython-input-39-68b225acfa4a> in <module>()
----> 1 socket_server.accept()
c:\users\admin\appdata\local\programs\python\python36-32\lib\socket.py in accept(self)
203 For IP sockets, the address info is a pair (hostaddr, port).
204 """
--> 205 fd, addr = self._accept()
206 # If our type has the SOCK_NONBLOCK flag, we shouldn't pass it onto the
207 # new socket. We do not currently allow passing SOCK_NONBLOCK to
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
#客户端连接
In [40]: conn, addr = socket_server.accept() #有连接则正确返回
In [41]: conn.recv(1024) #没有数据发送就会引发BlockingIOError异常
---------------------------------------------------------------------------
BlockingIOError Traceback (most recent call last)
<ipython-input-41-3b8d10a9535d> in <module>()
----> 1 conn.recv(1024)
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。
非阻塞IO模型
操作系统在处理第一个客户端套接字的数据传输服务时,仍可处理其他客户端的请求,而不是一直阻塞在第一个客户端。
使用非阻塞套接字实现并发
上节的套接字只能处理一个客户端请求,非阻塞套接字则能实现多个客户端的同时连接并传输数据。
方法:设置监听套接字和客户端套接字为非阻塞,使用客户端套接字队列,遍历队列进行数据传输
服务端
import socket
sock = socket.socket() #生成套接字
sock.setblocking(False) #设置非阻塞
sock.bind(('0.0.0.0',8800)) #绑定
sock.listen(5) #设置监听数量
conn_que=[] #客户端套接字队列
while True: #循环查询是否有客户端连接
try:
conn, addr = sock.accept() #有连接则设置非阻塞,加入套接字队列
print(addr, '链接')
conn.setblocking(False)
conn_que.append(conn)
except Exception as e: #无连接pass继续查询
pass
new_que=[i for i in conn_que] #复制套接字队列到新列表,以免后续的删除操作错误(可以试一下队
列删除操作)
for con in new_que: #查询每个客户端的数据传输
try:
date = con.recv(1024)
if date:
print(date)
con.send(date)
else:
print('断开连接', con)
con.close()
conn_que.remove(con)
except Exception as e:
pass
客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8800))
while True:
data = input('please write the message:')
client.send(data.encode())
print(client.recv(1024).decode())
if data == 'Q' or data == 'q':
break
client.close()
运行结果