IO多路复用——单程

IO多路复用——单程_第1张图片
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)

你可能感兴趣的:(IO多路复用——单程)