关键词搜索

源码搜索 ×
×

Python select.select 模块通信全过程详解

发布2019-11-15浏览3955次

详情内容

一、理解 select.select

要理解select.select模块其实主要就是要理解它的参数, 以及其三个返回值。

select()方法接收并监控3个通信列表, 第一个是所有的输入的data,就是指外部发过来的数据,第2个是监控和接收所有要发出去的data(outgoing data),第3个监控错误信息

readable, writable, exceptional = select.select(inputs, outputs, inputs)

    第一个参数就是服务器端的socket, 第二个是我们在运行过程中存储的客户端的socket, 第三个存储错误信息。

    重点是在返回值, 第一个返回的是可读的 list, 第二个存储的是可写的 list, 第三个存储的是错误信息的 list。

    这个也不必深究, 看看代码自己分析下就能有大概理解。

    二、示例

    1. 服务器端
    # coding: utf-8
    import select
    import socket
    import Queue
    from time import sleep
    
    
    # Create a TCP/IP
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setblocking(False)
    
    # Bind the socket to the port
    server_address = ('localhost', 8090)
    print ('starting up on %s port %s' % server_address)
    server.bind(server_address)
    
    # Listen for incoming connections
    server.listen(5)
    
    # Sockets from which we expect to read
    inputs = [server]
    
    # Sockets to which we expect to write
    # 处理要发送的消息
    outputs = []
    
    # Outgoing message queues (socket: Queue)
    message_queues = {}
    
    while inputs:
        # Wait for at least one of the sockets to be ready for processing
        print ('waiting for the next event')
        # 开始select 监听, 对input_list 中的服务器端server 进行监听
        # 一旦调用socket的send, recv函数,将会再次调用此模块
        readable, writable, exceptional = select.select(inputs, outputs, inputs)
    
        # Handle inputs
        # 循环判断是否有客户端连接进来, 当有客户端连接进来时select 将触发
        for s in readable:
            # 判断当前触发的是不是服务端对象, 当触发的对象是服务端对象时,说明有新客户端连接进来了
            # 表示有新用户来连接
            if s is server:
                # A "readable" socket is ready to accept a connection
                connection, client_address = s.accept()
                print ('connection from', client_address)
                # this is connection not server
                connection.setblocking(0)
                # 将客户端对象也加入到监听的列表中, 当客户端发送消息时 select 将触发
                inputs.append(connection)
    
                # Give the connection a queue for data we want to send
                # 为连接的客户端单独创建一个消息队列,用来保存客户端发送的消息
                message_queues[connection] = Queue.Queue()
            else:
                # 有老用户发消息, 处理接受
                # 由于客户端连接进来时服务端接收客户端连接请求,将客户端加入到了监听列表中(input_list), 客户端发送消息将触发
                # 所以判断是否是客户端对象触发
                data = s.recv(1024)
                # 客户端未断开
                if data != '':
                    # A readable client socket has data
                    print ('received "%s" from %s' % (data, s.getpeername()))
                    # 将收到的消息放入到相对应的socket客户端的消息队列中
                    message_queues[s].put(data)
                    # Add output channel for response
                    # 将需要进行回复操作socket放到output 列表中, 让select监听
                    if s not in outputs:
                        outputs.append(s)
                else:
                    # 客户端断开了连接, 将客户端的监听从input列表中移除
                    # Interpret empty result as closed connection
                    print ('closing', client_address)
                    # Stop listening for input on the connection
                    if s in outputs:
                        outputs.remove(s)
                    inputs.remove(s)
                    s.close()
    
                    # Remove message queue
                    # 移除对应socket客户端对象的消息队列
                    del message_queues[s]
    
        # Handle outputs
        # 如果现在没有客户端请求, 也没有客户端发送消息时, 开始对发送消息列表进行处理, 是否需要发送消息
        # 存储哪个客户端发送过消息
        for s in writable:
            try:
                # 如果消息队列中有消息,从消息队列中获取要发送的消息
                message_queue = message_queues.get(s)
                send_data = ''
                if message_queue is not None:
                    send_data = message_queue.get_nowait()
                else:
                    # 客户端连接断开了
                    print "has closed "
            except Queue.Empty:
                # 客户端连接断开了
                print "%s" % (s.getpeername())
                outputs.remove(s)
            else:
                # print "sending %s to %s " % (send_data, s.getpeername)
                # print "send something"
                if message_queue is not None:
                    s.send(send_data)
                else:
                    print "has closed "
                # del message_queues[s]
                # writable.remove(s)
                # print "Client %s disconnected" % (client_address)
    
        # # Handle "exceptional conditions"
        # 处理异常的情况
        for s in exceptional:
            print ('exception condition on', s.getpeername())
            # Stop listening for input on the connection
            inputs.remove(s)
            if s in outputs:
                outputs.remove(s)
            s.close()
    
            # Remove message queue
            del message_queues[s]
    
        sleep(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
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    1. 客户端
    # coding: utf-8
    import socket
    
    
    messages = ['This is the message ', 'It will be sent ', 'in parts ', ]
    
    server_address = ('localhost', 8090)
    
    # Create aTCP/IP socket
    
    socks = [socket.socket(socket.AF_INET, socket.SOCK_STREAM), socket.socket(socket.AF_INET,  socket.SOCK_STREAM), ]
    
    # Connect thesocket to the port where the server is listening
    
    print ('connecting to %s port %s' % server_address)
    # 连接到服务器
    for s in socks:
        s.connect(server_address)
    
    for index, message in enumerate(messages):
        # Send messages on both sockets
        for s in socks:
            print ('%s: sending "%s"' % (s.getsockname(), message + str(index)))
            s.send(bytes(message + str(index)).decode('utf-8'))
        # Read responses on both sockets
    
    for s in socks:
        data = s.recv(1024)
        print ('%s: received "%s"' % (s.getsockname(), data))
        if data != "":
            print ('closingsocket', s.getsockname())
            s.close()
    
      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

    写代码过程中遇到了两个问题, 一是如何判断客户端已经关闭了socket连接, 后来自己分析了下, 如果关闭了客户端socket, 那么此时服务器端接收到的data就是’’, 加个这个判断。二是如果服务器端关闭了socket, 一旦在调用socket的相关方法都会报错, 不管socket是不是用不同的容器存储的(意思是说list_1存储了socket1, list_2存储了socket1, 我关闭了socket1, 两者都不能在调用这个socket了)

    服务端:
    在这里插入图片描述
    客户端:
    在这里插入图片描述

    相关技术文章

    点击QQ咨询
    开通会员
    返回顶部
    ×
    微信扫码支付
    微信扫码支付
    确定支付下载
    请使用微信描二维码支付
    ×

    提示信息

    ×

    选择支付方式

    • 微信支付
    • 支付宝付款
    确定支付下载