2017-08-25 45 views
0

我一直在尋找和處理這個問題一個星期。我有客戶端代碼導致select()返回一個實際上從外部原因關閉的套接字,拋出一個錯誤9 BAD FILE DESCRIPTOR,但是我測試了不同python文件中的代碼,並且無法將其報錯。我曾嘗試過一百萬件事。繼承自服務器的代碼片段:Python隨機關閉套接字似乎

注意:這將適用於幾次迭代,然後突然中止,它在message_queue中出錯,因爲由於文件描述符中斷而導致的關鍵錯誤即使是消息/沒有消息具有密鑰爲那個插座提供。

#Create the socket to communicate with uWSGI applications 
server_address = ('localhost', 10001) 
server = create_server_socket(server_address) 
#Sockets which we expect to read on from select() 
input_sockets = [server] 
#Sockets which we expect to write to from select() 
output_sockets = [] 
#Message buffer dicitonary for outgoing messages 
message_queue = {} 
#Now wait for connections endlessly 
while input_sockets: 
    print >> sys.stderr, "Waiting for the next event..." 
    readable, writable, exceptional = select.select(input_sockets, output_sockets, input_sockets) 
    #Handle input_sockets 
    for s in readable: 
     #Server socket is available for reading now 
     if s is server: 
      #Create a connection and address object when incoming request is recieved 
      connection, client_addr = s.accept() 
      print >> sys.stderr, "Connection recieved from %s!" % (client_addr,) 
      #Set client connection to non blocking as well 
      connection.setblocking(0) 
      #Add this socket to input sockets as it will read for client data 
      input_sockets.append(connection) 
      #Give connection a queue for sending messages to it 
      message_queue[connection] = Queue.Queue() 
     #A client has sent data so we can handle its request 
     else: 
      #Pull data from the client 
      data = "" 
      try: 
       while True: 
        message = s.recv(1024) 
        if not message: 
         break 
        data += message 
      except Exception as e: 
       print str(e) 
      if data: 
       #Readable client socket has data 
       print >> sys.stderr, 'Recieved "%s" from %s' % (data, s.getpeername()) 
       message_queue[s].put(data) 

       #Add output channel now to send message 
       if s not in output_sockets: 
        output_sockets.append(s) 
      #There is no data to be read, socket must be closed 
      else: 
       print >> sys.stderr, 'Closing', client_addr,'after recieving no data.' 
       #Stop listening for input on the socket 
       if s in output_sockets: 
        output_sockets.remove(s) 
       input_sockets.remove(s) 
       #Close the connection 
       s.close() 
       del message_queue[s] 
    #Handle writable connections  
    for s in writable: 
     if s: 
      try: 
       next_message = message_queue[s].get_nowait() 
      except: 
       print >> sys.stderr, 'No data to send for', s.getpeername() 
       output_sockets.remove(s) 
      else: 
       try: 
        print >> sys.stderr, 'Sending "%s" to %s' % (next_message, s.getpeername()) 
        s.sendall(next_message) 
       except: 
        print >> sys.stderr, 'No data to send for', s.getpeername() 
        output_sockets.remove(s) 
       #s.sendall('EOF:[email protected]#$:EOF') 
    #Now handle any exceptions 
    for s in exceptional: 
     print >> sys.stderr, 'Handling exception on ', s.getpeername() 
     input_sockets.remove(s) 
     if s in output_sockets: 
      output_sockets.remove(s) 
     s.close() 
     #Remove any messages 
     del message_queue[s] 

客戶端:

messages = [ 'This is the message. ', 
     'It will be sent ', 
     'in parts.', 
     ] 
server_address = ('localhost', 10001) 

# Create a TCP/IP socket 
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM), 
      socket.socket(socket.AF_INET, socket.SOCK_STREAM), 
      ] 

# Connect the socket to the port where the server is listening 
print >>sys.stderr, 'connecting to %s port %s' % server_address 
for s in socks: 
    s.connect(server_address) 
for message in messages: 

    # Send messages on both sockets 
    for s in socks: 
     print >>sys.stderr, '%s: sending "%s"' % (s.getsockname(), message) 
     s.send(message) 

    # Read responses on both sockets 
    for s in socks: 
     data = s.recv(1024) 
     print >>sys.stderr, '%s: received "%s"' % (s.getsockname(), data) 
     if not data: 
      print >>sys.stderr, 'closing socket', s.getsockname() 
      s.close() 

注:此客戶端僅僅是測試,並開始傳遞消息。

+0

這是很多代碼。究竟哪一行你會得到錯誤? –

+0

在服務器下可寫,我從隊列中得到一個關鍵錯誤,因爲FD壞了,而不是用作密鑰的同一個套接字。然後,如果我捕捉所有錯誤並繼續原來罪魁禍首其實是ERRNO 9的位置:對於s的寫: 如果s: 嘗試: next_message = message_queue [S] .get_nowait() 除外: 打印>> SYS。標準錯誤, '沒有要發送的數據爲',s.getpeername() output_sockets.remove(S) –

+0

選擇不應該返回一個BADF是否正確? –

回答

1

當套接字以可讀和可寫方式返回時,代碼中存在爭用,並且由於讀返回0字節而關閉套接字。在這種情況下,你從input_socketsoutput_socketsmessage_queue卸下插槽,但關閉socket仍處於writable,因此它會嘗試寫它選擇環路的同一迭代內。

我不知道如果是這樣的比賽,你會看到,因爲你既沒有顯示調試輸出不是你說,你絆倒這個EBADF。要跟蹤類似的問題下來,我建議你在哪裏關閉套接字,並在那裏你嘗試處理一個插座,因爲它是讀寫,讓你真正找到了比賽的確切地點有更多的調試信息,以增加自己的代碼在看的時候調試輸出。

+0

生病試試這個早上,因爲這是有道理的,生病看是否removong插座在讀從兩個可寫可讀AMD拆除和給予好評 –

+0

我認爲加上一個writable.remove(S)將解決這個問題! –

+0

非常感謝你爲我的朋友敏捷+100 –