QQ截图20180329004318.png
套接字多线连接
这一节,我们学习的是,如何让服务器的一个套接字可以接待多个客户端。
有如下两种方法:
1. "设置"TIMEOUT(0) --- 非阻塞套接字
2. 操作系统监控套接字状态 --- IO多路复用选择器
设置TIMEOUT(0) --- (非阻塞套接字)
0. 原理
放弃套接字的阻塞,直接若阻塞直接报错。
1. 套接字的子代继承
套接字对象:
第一代:socket.socket() --> 该对象用于绑定服务端 端口与地址
第二代:第一代.accept()[0] --> 该对象绑定客户端与服务端的连接服务
2. 套接字的非阻塞化
设置:套接字对象.setblocking(False)
相当于timeout(0)
若阻塞-> ERROR:BlockingIOError
在使用时,需注意对两代的套接字对象都需进行非阻塞化
3. 套接字多线连接实现
import socket
import time
socSer = socket.socket()
socSer.setblocking(False)
socSer.bind( ('127.0.0.1', 8994) )
socSer.listen(5)
Cli = []
while True:
"""测试服务器:打印收到的额信息
流程:
- try,except,else:检查是否有新的客户端连接进来
- finally:检测所有已经连接进来的客户端是否有发新的消息
- if:当没有客户端连接时,询问是否需要关闭服务器
"""
try:
conn, add = socSer.accept()
conn.setblocking(False)
except BlockingIOError:
pass
else:
print('客户端{}已连上'.format(add))
Cli.append(conn)
finally:
for c in Cli:
"""
# 检测每个已经连接进来的客户端c
# try-except-else:检测当前客户端是否有信息发送进来,若无则跳过,若有:
# else:当消息为空时,表示该客户端断开,关闭杆连接。
# 当消息不为空时,打印消息来源于,与信息
#
"""
try:
data = c.recv(1024)
except BlockingIOError:
pass
else:
if data:
print('客户端{}发来信息:{}'.format(c, data))
else:
print('客户端{}关闭-----'.format(c))
c.close()
Cli.remove(c)
if not Cli:
chose = input('客户端已空是否继续?')
if chose == 'y':
break
socSer.close()
print('关闭端口')
检测套接字状态变化 --- (IO多路复用选择器)
0. 原理
操作系统检测注册事件的变化,将发生变化的事件结果返回到列表中。
callback(objfile)是对select的一次释放,若释放,
1. 过程
a. 实例化一个选择器
Linux:
selectors.EpollSelector()
Win/Linux:
selector.DefaultSelector()
为什么叫选择器?
>因为这个对象的最终作用是, 选中 发生变化的注册事件
b. 注册该选择器可能有的事件(简称:注册事件)
选择器.register( para1, para2, para3 ) 返回-->打包对象
# para1:触发函数(以:套接字socket为例)
# para2:触发条件(以:事件可读"selector.EVENT_READ"为例)
# para3:回调函数
c. 选择器选择事件
选择器.select()
#返回列表:[(注册事件中的打包对象),1]
#打包对象的选择,分以下三种情况:
#>情况一:还没有满足"触发条件"的事件,阻塞。
#>情况二:曾有满足"触发条件"的事件,但没有新触发的事件,保持原对象
#>情况三:返回新的触发事件的打包对象
#释放:由callback(para1)/objfile(para3)任一资源被调用,选择器.select()因资源释放后会重回阻塞
o. 打包对象的利用
打包对象的调用:
函数:列表[0][0].data (以:回调函数 为例)
参数:列表[0][0].fileobj (以:套接字对象 为例)
则可以:函数(参数)
2. 套接字多线连接实现
# -*- coding:utf-8 -*-
"""
使用EpollSelector 实现一个并发的服务器
"""
import selectors
import socket
s = socket.socket()
s.bind(('127.0.0.1', 9996))
s.listen(5)
def connect(ser):
conn, _ = ser.accept()
print(conn)
selector.register(conn, selectors.EVENT_READ, read)
def read(conn):
data = conn.recv(1024).decode('utf-8')
if data:
print(data)
else:
selector.unregister(conn)
conn.close()
selector = selectors.EpollSelector()
selector.register(s, selectors.EVENT_READ, connect)
while True:
e = selector.select()
callback = e[0][0].data
para = e[0][0].fileobj
callback(para)
网友评论