zoukankan      html  css  js  c++  java
  • 使用select异步IO实现socketserver服务器 源码剖析

    #_*_coding:utf-8_*_
    #这是一个echo server,客户端消息,服务端回复相同的消息
    import select, socket, sys, queue
     
    # Create a TCP/IP socket
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)		#对socket进行实例化,拿到server句柄
    server.setblocking(False)		#设置server为不阻塞
     
    # Bind the socket to the port
    server_address = ('localhost', 10000)		#将主机名,端口号赋值给server_address
    print(sys.stderr, 'starting up on %s port %s' % server_address)		#如有错误信息,输出,并打印strting up...
    server.bind(server_address)		#绑定服务器ip端口号给socket实例化后的句柄
     
    # Listen for incoming connections
    server.listen(5)		#允许最大监听链接5
     
    # Sockets from which we expect to read
    inputs = [ server ]		#设置客户端和服务端的socket句柄列表,后续都会append到其中
     
    # Sockets to which we expect to write
    outputs = [ ]		#设置echo给客户端的socket句柄,后续会append到其中
     
    message_queues = {}		#设置消息队列字典,后续存放
    while inputs:		#维护所有服务端和客户端过来的连接
     
        # Wait for at least one of the sockets to be ready for processing
        print( '
    waiting for the next event')
        readable, writable, exceptional = select.select(inputs, outputs, inputs)		#执行select.select方法,将输入的句柄、输出的句柄和错误信息,分别赋值给readable、writeable、exceptional
        # Handle inputs
        for s in readable:		#遍历服务端和客户端请求过来的socket句柄
     
            if s is server:		#如果是客户端连接的句柄
                # A "readable" server socket is ready to accept a connection
                connection, client_address = s.accept()		#接收连接请求
                print('new connection from', client_address)		#打印新请求连接信息
                connection.setblocking(False)		#设置为连接非阻塞,否则即使将会hang住
                inputs.append(connection)		#将客户端连接的句柄append到inputs列表,下一次select的时候能够检测这个连接
     
                # Give the connection a queue for data we want to send
                message_queues[connection] = queue.Queue()		#连接进来后,把发的消息放到这个队列;以连接的句柄作为key,来以字典形式存放队列信息,保证每个连接的句柄发送的数据都是隔离的
            else:		#如果是服务端连接的句柄
                data = s.recv(1024)		#接收客户端发来的数据
                if data:		#如果客户端发过来的数据不为空
                    # A readable client socket has data
                    print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) )		#如果有错误,打印错误信息
                    message_queues[s].put(data)		#放到以客户端socket的句柄为key的字典里,数据作为value存放
                    # Add output channel for response
                    if s not in outputs:		如果当前客户端的连接句柄没有在outputs这个待发送的列表中
                        outputs.append(s)		#先不给客户端发送其发来的数据,因为如果客户端没有空闲,就会造成阻塞。先把要发送的数据append到outputs自己维护的这个列表,下次select的时候,这个列表就不为空了,那个时候再给客户端发送数据
                else:		#如果客户端发过来的数据是空
                    # Interpret empty result as closed connection
                    print('closing', client_address, 'after reading no data')		#打印关闭信息
                    # Stop listening for input on the connection
                    if s in outputs:		#如果当前客户端的句柄,在要回复数据的列表中
                        outputs.remove(s)		#既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
                    inputs.remove(s)		#inputs中也删除掉
                    s.close()		#把这个连接关闭掉
     
                    # Remove message queue
                    del message_queues[s]		#删掉客户端这个socket发过来的数据
        # Handle outputs
        for s in writable:		#这是要给客户端回复数据的列表,这列表是自己维护的
            try:
                next_msg = message_queues[s].get_nowait()		#取出当前客户端的socket句柄对应的消息
            except queue.Empty:		#如果这个客户端的socket句柄对应的消息是空的话
                # No messages waiting so stop checking for writability.
                print('output queue for', s.getpeername(), 'is empty')		#打印空队列消息
                outputs.remove(s)		#移除这个客户端的socket句柄
            else:		#如果没有报错的话
                print( 'sending "%s" to %s' % (next_msg, s.getpeername()))		#打印发送数据
                s.send(next_msg)		#服务器真正给客户端发送之前客户端发过来的数据
        # Handle "exceptional conditions"
        for s in exceptional:		#在客户端断开的情况下,会赋值给exceptional
            print('handling exceptional condition for', s.getpeername() )		#打印错误信息
            # Stop listening for input on the connection
            inputs.remove(s)		#删除客户端socket对应的句柄
            if s in outputs:		#如果在要发的消息列表中存在客户端的socket句柄
                outputs.remove(s)		#删除客户端的socket句柄
            s.close()		#服务器关闭这个客户端先断开的连接
     
            # Remove message queue
            del message_queues[s]		#删除掉客户端socket句柄对应的消息
  • 相关阅读:
    7年.NET面试Java的尴尬历程
    服务挂后Dump日志
    并发中如何保证缓存DB双写一致性(JAVA栗子)
    如何通过Visual Studio来管理我们的数据库项目
    无需Get更多技能,快速打造一个可持久化的任务调度
    Dapper Use For Net
    2014年——新的开始,新的人生
    途牛网站无线架构变迁实践
    windows 下解决 Time_Wait 和 CLOSE_WAIT 方法
    System.Data.DbType 与其它DbType的映射关系
  • 原文地址:https://www.cnblogs.com/stefan-liu/p/5285728.html
Copyright © 2011-2022 走看看