04协程番外:IO多路复用之select服务器案例第二版

"""
select服务器,第二版
"""
import select
import socket
import time
import queue

SERVER_HOST=('127.0.0.1',59999)
SECRET_KEY_SHUTDOWN_SERVER='PASSWORD:[email protected]'


class Select_Server_02:
    def __init__(self):
        #创建服务器的套接字
        self.server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        #我们为server_socket绑定服务器IP
        self.server_socket.bind(SERVER_HOST)
        """
        此处我们将服务器套接字设置为非阻塞的,这样设置的原因如下
            我们上一版server_socket是阻塞的,
            在server_socket就绪后,我们从select返回的就绪队列中获取server_socket并调用它的阻塞方法(比如accept()方法接受新的请求),
            但是如果与客户端之间网络连接突然变得异常慢,整个大循环将阻塞住,
            为了避免这种情况,我们将server_socket设置为非阻塞的
        """
        self.server_socket.setblocking(False)
        #将这个套接字置为监听套架子
        self.server_socket.listen(100)
        #待监听可读队列
        self.read_list=[]
        self.read_list.append(self.server_socket) #将当前server_socket加入待监听可读队列
        #待监听可写队列
        self.write_list=[]
        #客户端socket传入参数
        self.client_socket_args={}

    def handle_request(self,arg):
        """
        处理客户端传入请求数据
        """
        return "<<<<<服务器端口在{}时的处理结果:{}>>>>>".format(time.time(),arg)

    def run(self):
        """
        主循环,程序运行在其中
        """
        while True:
            #创建待监听异常的资源列表(此处是所有待监听对象)
            exception_list=self.read_list+self.write_list
            #获取select监听结果,,,得到读就绪队列,写就绪队列,异常就绪队列
            readables,writeables,exceptionals=select.select(self.read_list,self.write_list,exception_list)

            #接下来,我们分别处理这三个就绪队列
            #处理读就绪队列
            for readable in readables:
                #如果就绪socket是当前服务器socket
                if readable is self.server_socket:
                    #获取客户端和客户端地址
                    client_socket,client_address=self.server_socket.accept()
                    print('服务器>{}<:服务器件接收到新的socket {}'.format(time.time(),readable.getsockname()))
                    '''
                    此处我们将客户端套接字设置为非阻塞的,这样设置的原因如下
                        我们上一版client_socket是阻塞的,
                        在client_socket就绪后,我们从select返回的就绪队列中获取client_socket并调用它的阻塞方法(比如recv(),send()数据传送方法)
                        但是如果与客户端之间网络链接突然变慢,整个大循环将阻塞住,
                        为了避免这种情况,我们将client_socket设置为非阻塞的                 
                    '''
                    client_socket.setblocking(False)
                    #将客户端socket放在待监听可读队列
                    self.read_list.append(client_socket)
                else: #接下来所有可读就绪的socket都是client_socket[客户端socket]
                    recv_data=readable.recv(1024).decode('utf-8')   #接收客户端的发送过来的参数
                    print('----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:{}'.format(recv_data==''))
                    if recv_data==SECRET_KEY_SHUTDOWN_SERVER: #如果客户端传入的是服务器关闭密令
                        print('服务器>{}<:服务器接收来自 {} 的关闭指令 {}'.format(time.time(),readable.getsockname(),recv_data))
                        self.close() #关闭服务器
                        return #结束主循环

                    elif recv_data=='': #如果客户端传入为空,表示自己关闭的位置
                        print('服务器>{}<:服务器关闭连接 {}'.format(time.time(),readable.getsockname()))
                        #关闭这个可读就绪socket
                        try:
                            readable.close()
                        except:
                            pass
                        #接下来我们移除与这个socket相关的
                        #从待监听可读队列移除这个socket
                        if readable in self.read_list:
                            self.read_list.remove(readable)
                        #从待监听可写队列移除这个socket
                        if readable in self.write_list:
                            self.write_list.remove(readable)
                        #从写就绪队列中移除这个socket
                        if readable in writeables:
                            writeables.remove(readable)
                        #从异常就绪队列移除这个socket
                        if readable in exceptionals:
                            exceptionals.remove(readable)
                    else: #客户端传入的普通参数
                        print('服务器>{}<:接受 {} 发送过来的参数 {}'.format(time.time(),readable.getsockname(),recv_data))
                        #将客户端传入参数放入客户端参数字典
                        self.client_socket_args[readable]=recv_data
                        #将这个readable放置入待监听可写队列
                        self.write_list.append(readable)

            #处理写就绪队列
            for writeable in writeables: #writeable只能是客户端socket
                #获取关于该客户端socket的参数
                client_socket_data=self.client_socket_args[writeable]
                client_socket_response=self.handle_request(client_socket_data)
                print('服务器>{}<:发送数据 {} 给 {}'.format(time.time(),client_socket_response,readable.getsockname()))
                #向该客户端socket发送参数
                writeable.send(client_socket_response.encode('utf-8'))
                #发送完参数后将客户端socket的从可写队列移除(只有客户端向服务器发送一个普通参数后我们再给这个就客户端发送响应)
                self.write_list.remove(writeable)

            #处理异常就绪队列(发生异常的资源集合)
            for exceptional in exceptionals:
                #将发生异常的所有socket从待监听队列中移除,如果server_socket发生异常则我们关闭服务器
                if exceptionals==self.server_socket:
                    self.close()
                    return
                #关闭并清除发生异常的socket
                try:
                    exceptional.close()
                    print('服务器>{}<:关闭并移除发生异常的scoket:{}'.format(time.time(),exceptional.getsockname()))
                except:
                    pass
                #从待监听队列中移除对应socket
                if exceptional in self.write_list:
                    self.write_list.remove(exceptional)
                if exceptional in self.read_list:
                    self.read_list.remove(exceptional)

    def close(self):
        #关闭所有待监听读队列与待监听队列中所有的socket
        for item_socket in self.read_list+self.write_list:
            try:
                item_socket.close()
            except:
                pass
        print('服务器>{}<:所有的待监听资源关闭完毕,服务器停止成功'.format(time.time()))

def client_socket_02(info,num):
    final_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    final_client_socket.connect(SERVER_HOST)
    for _ in range(num):
        final_client_socket.send(info.encode('utf-8'))
        print("     {}收到来自{}的响应:{}".format(final_client_socket.getsockname(),SERVER_HOST,final_client_socket.recv(1024).decode("utf-8")))
    final_client_socket.send(''.encode('utf-8'))
    print('     {}:发送空字符串给:{}'.format(final_client_socket.getsockname(),SERVER_HOST))

    try:
        final_client_socket.close()
    except:
        pass


import multiprocessing
def main_01():
    #先启动服务器
    server_socket=Select_Server_02()
    multiprocessing.Process(target=server_socket.run).start()

    #启动测试客户端
    multiprocessing.Process(target=client_socket_02,args=('测试客户端1',2)).start()
    multiprocessing.Process(target=client_socket_02,args=('测试客户端2',1)).start()
    multiprocessing.Process(target=client_socket_02,args=('测试客户端3',3)).start()


    #创建一个客户端发送一个关闭指令给服务器
    time.sleep(5) #这句话很重要,你不要一上来就把服务器关了
    final_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    final_client_socket.connect(SERVER_HOST)
    final_client_socket.send(SECRET_KEY_SHUTDOWN_SERVER.encode('utf-8'))

    try:
        final_client_socket.close()
    except:
        pass
    print('-------------------------->>>>>>>>>>>>>>>>执行关闭服务器完毕')
'''
执行:

    main_01()
    
执行结果:

    服务器>1548492096.1225634<:服务器件接收到新的socket ('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.1227622<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端1
    服务器>1548492096.1228628<:发送数据 <<<<<服务器端口在1548492096.122849时的处理结果:测试客户端1>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51360)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.122849时的处理结果:测试客户端1>>>>>
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.125385<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端1
    服务器>1548492096.1256537<:发送数据 <<<<<服务器端口在1548492096.1256306时的处理结果:测试客户端1>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51360)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.1256306时的处理结果:测试客户端1>>>>>
         ('127.0.0.1', 51360):发送空字符串给:('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:True
    服务器>1548492096.1265924<:服务器关闭连接 ('127.0.0.1', 59999)
    服务器>1548492096.1267126<:服务器件接收到新的socket ('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.1267486<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端3
    服务器>1548492096.1267707<:发送数据 <<<<<服务器端口在1548492096.1267674时的处理结果:测试客户端3>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51362)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.1267674时的处理结果:测试客户端3>>>>>
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.12692<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端3
    服务器>1548492096.126953<:发送数据 <<<<<服务器端口在1548492096.1269476时的处理结果:测试客户端3>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51362)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.1269476时的处理结果:测试客户端3>>>>>
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.12704<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端3
    服务器>1548492096.1270642<:发送数据 <<<<<服务器端口在1548492096.1270602时的处理结果:测试客户端3>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51362)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.1270602时的处理结果:测试客户端3>>>>>
         ('127.0.0.1', 51362):发送空字符串给:('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:True
    服务器>1548492096.1271636<:服务器关闭连接 ('127.0.0.1', 59999)
    服务器>1548492096.1276844<:服务器件接收到新的socket ('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492096.127718<:接受 ('127.0.0.1', 59999) 发送过来的参数 测试客户端2
    服务器>1548492096.127733<:发送数据 <<<<<服务器端口在1548492096.1277306时的处理结果:测试客户端2>>>>> 给 ('127.0.0.1', 59999)
         ('127.0.0.1', 51364)收到来自('127.0.0.1', 59999)的响应:<<<<<服务器端口在1548492096.1277306时的处理结果:测试客户端2>>>>>
         ('127.0.0.1', 51364):发送空字符串给:('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:True
    服务器>1548492096.1278462<:服务器关闭连接 ('127.0.0.1', 59999)
    -------------------------->>>>>>>>>>>>>>>>执行关闭服务器完毕
    服务器>1548492101.126528<:服务器件接收到新的socket ('127.0.0.1', 59999)
    ----------------------------------------------------->>>>>>>>>>>>>>接受参数为空:False
    服务器>1548492101.126574<:服务器接收来自 ('127.0.0.1', 59999) 的关闭指令 PASSWORD:[email protected]
    服务器>1548492101.1266284<:所有的待监听资源关闭完毕,服务器停止成功
    
'''

你可能感兴趣的:(04协程番外:IO多路复用之select服务器案例第二版)