天天看點

Python select.select 子產品通信全過程詳解

文章目錄

    • 一、了解 select.select
    • 二、示例

一、了解 select.select

要了解

select.select

子產品其實主要就是要了解它的參數, 以及其三個傳回值。

select()

方法接收并監控3個通信清單, 第一個是所有的輸入的data,就是指外部發過來的資料,第2個是監控和接收所有要發出去的data(outgoing data),第3個監控錯誤資訊

第一個參數就是伺服器端的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)
           
  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()
           

寫代碼過程中遇到了兩個問題, 一是如何判斷用戶端已經關閉了socket連接配接, 後來自己分析了下, 如果關閉了用戶端socket, 那麼此時伺服器端接收到的data就是’’, 加個這個判斷。二是如果伺服器端關閉了socket, 一旦在調用socket的相關方法都會報錯, 不管socket是不是用不同的容器存儲的(意思是說list_1存儲了socket1, list_2存儲了socket1, 我關閉了socket1, 兩者都不能在調用這個socket了)

服務端:

Python select.select 子產品通信全過程詳解

用戶端:

Python select.select 子產品通信全過程詳解