2015-10-15 189 views
2

我檢查了stackoverflow.com上的幾個類似線程,我想我可能需要爲我的服務器腳本打開非阻塞套接字。因爲,我不確定這是否是問題標題可能是錯誤的解決方案。讓我解釋我的問題。服務器的非阻塞套接字

服務器應用程序等待連接,一旦客戶端連接,它會詢問服務器ID,然後客戶端將詢問服務器配置,然後客戶端將命令發送到服務器以開始測量傳輸。這裏是我的代碼的簡化版本:

def conn_handler(self, connection, address): 
    self.logger.info("[%d] - Connection from %s:%d", 10, address[0], address[1]) 

    sending_measurements_enabled = False 
    try: 
     while True: 
      data = connection.recv(2048) 

      if data: 
       command = get_command_from_data(data) 
      else: 
       command = None 

      if command == 'start': 
       sending_measurements_enabled = True 
      elif command == 'stop': 
       break 
      elif command == 'id': 
       connection.sendall(self.id) 
      elif command == 'cfg': 
       connection.sendall(self.cfg) 

      if sending_measurements_enabled: 
       connection.sendall(measurement) 

    except Exception as e: 
     print(e) 
    finally: 
     connection.close() 
     print("Connection closed") 

這裏是客戶端腳本:

try: 

    sock.sendall(get_id_command)  

    data = sock.recv(2048) # Do I need to wait for response? 
    print(data) 

    sock.sendall(get_conf_command) 

    data = sock.recv(2048) 
    print(data) 

    sock.sendall(start_sending_measurements) 
    data = sock.recv(2048) 
    print(data) 

    while True: 
     sock.sendall(bytes('I do not want this', 'utf-8')) # I would like to keep receiving measurements without this 
     data = sock.recv(2048) 
     print(data) 

finally: 
    print('Closing socket...') 
    sock.close() 

這裏是我的問題:

當我運行客戶端和發送命令,獲取ID服務器會返回ID消息,然後客戶端將發送命令獲取配置,服務器將返回配置消息,但當我發送start命令服務器將發送只有一個測量,我猜connection.recv(2048)將阻止執行,直到服務器獲得另一個comman d。所以,我在while True:循環中添加了這行代碼,它將繼續發送(不必要的,無效的)命令,服務器將繼續發送測量結果。

如何解決這個問題,而不需要一直從客戶端發送命令。我希望只能發送一個命令start,服務器將繼續發送測量結果,並且僅在客戶端發送stop命令時停止。 此外,如果服務器在發送測量結果時收到idcfg命令,它將首先發送idcfg並繼續發送測量結果。

+0

的'else'爲'如果數據:'應該像命令「停止」,因爲空'data'在這一點意味着對方已經關閉了連接因此不再發送任何東西。 – BlackJack

+0

您也可以使用線程發送測量值,而不是非阻塞套接字。 – BlackJack

+0

@BlackJack僅當客戶端試圖終止連接時才發送空包。如果客戶端沒有發送任何東西(在下面的代碼中)'如果可讀:'塊內的代碼永遠不會被執行,如果客戶端發送空包,這意味着連接應該被關閉? – sstevan

回答

0

在服務器循環調用select.select([connection], [connection], [connection])select模塊提供了更多的設施,所以選擇你最喜歡的)。如果套接字可讀,請閱讀命令並對其作出反應。如果套接字可寫(並且有數據請求),請發送測量結果。

+0

謝謝!有效。 – sstevan

0

萬一有人需要這樣的:

def conn_handler(self, connection, address): 
    self.logger.info("[%d] - Connection from %s:%d", 10, address[0], address[1]) 

    sending_measurements_enabled = False 
    try: 
     while True: 

      command = None 
      readable, writable, exceptional = select([connection], [], [], 0) 

      if readable: 
       data = connection.recv(2048) 

       if data: 
        command = get_command_from_data(data) 
        print("Received command %s", command) 


      if command == 'start': 
       sending_measurements_enabled = True 
      elif command == 'stop': 
       break 
      elif command == 'id': 
       connection.sendall(self.id) 
      elif command == 'cfg': 
       connection.sendall(self.cfg) 

      if sending_measurements_enabled: 
       connection.sendall(measurement) 

    except Exception as e: 
     print(e) 
    finally: 
     connection.close() 
     print("Connection closed") 
+0

如果'recv()'調用確實只讀取了命令的一部分,會發生什麼?這看起來不太健壯。 – BlackJack

+0

@BlackJack現在我有非常短的命令,一切正常。但我將不得不重構這個,不知道如何,因爲我沒有命令分隔符。我想我將不得不循環'connection.recv(BUFFER_SIZE)'直到我得到有效的命令? – sstevan

+0

或者您添加一個分隔符。或者使用固定大小。或固定大小的前綴長度信息。 – BlackJack