Fork me on GitHub

使用select异步IO实现socketserver服务器 源码剖析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#_*_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( '\nwaiting 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句柄对应的消息
posted @   stefan.liu  阅读(563)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示