2014-03-07 50 views
4

我使用here中的代碼測試UDP衝孔。它適用於Linux,但在Windows上報告錯誤。這裏就是發生錯誤的代碼片段:Windows上的python select.select()

while True: 
    rfds, _, _ = select([0, sockfd], [], []) # sockfd is a socket 
    if 0 in rfds: 
     data = sys.stdin.readline() 
     if not data: 
      break 
     sockfd.sendto(data, target) 
    elif sockfd in rfds: 
     data, addr = sockfd.recvfrom(1024) 
     sys.stdout.write(data) 

和錯誤信息:

Traceback (most recent call last): 
    File "udp_punch_client.py", line 64, in <module> 
    main() 
    File "udp_punch_client.py", line 50, in main 
    rfds, _, _ = select([0, sockfd], [], []) 
select.error: (10038, '') 

我知道這個錯誤有一些事情與select實現在Windows上,每個人都引用這一點:

注意 Windows上的文件對象是不可接受的,但套接字是。在Windows的 上,底層select()函數由WinSock 庫提供,並且不處理WinSock中不產生 的文件描述符。

所以,我有兩個問題:

  1. 是什麼0[0, sockfd]是什麼意思?這是一種經常使用的技術嗎?
  2. 如果select只適用於Windows的socket,如何使代碼與Windows兼容?

謝謝。

+1

'0'是'stdin'的'fd'。 [文件描述符](http://en.wikipedia.org/wiki/File_descriptor) – tmr232

+0

@ tmr232任何想法如何修改代碼? – laike9m

+0

@ J.F.Sebastian好吧 – laike9m

回答

4

不幸的是,select不會幫助您在一個線程中處理stdin和網絡事件,因爲select無法在Windows上使用流。你需要的是一種無障礙地閱讀stdin的方法。您可以使用:

  1. stdin的額外線程。這應該很好,是做這份工作的最簡單的方法。如果你需要的只是等待I/O事件,那麼Python線程支持是相當不錯的。
  2. A greenlet類似於gevent的機制,修補程序線程支持和標準庫的大部分I/O功能,以防止它們阻塞greenlet。還有像twisted這樣的庫(參見注釋)提供了非阻塞文件I/O。這種方式是最一致的,但它應該要求使用與您的框架相匹配的樣式(twistedgevent,差異不顯着)編寫整個應用程序。但是,我懷疑twisted包裝不能從Windows上的stdin(可以肯定他們可以在* nix上做到這一點,因爲它們可能使用相同的select)。
  3. 一些其他的伎倆。但是,大部分可能的技巧都相當難看。
+0

我會盡快接受你的回答。 – laike9m

+0

+1線程可能是Windows上最簡單的路線。還有'扭曲','asyncio'(鬱金香) – jfs

+0

應該看'asyncio',謝謝。至於「扭曲」,這是一個優秀的圖書館(我通常使用'twisted'或'gevent',就像兩者一樣)。是的,它具有文件類對象的包裝器。我真的認爲使用這樣的框架是最好的解決方案。但是,這不僅僅是對現有代碼的小修改。 – Ellioh

2

正如答案所暗示的,我創建了另一個線程來處理輸入流並且它可以工作。 以下是修改後的代碼:

sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 

def send_msg(sock): 
    while True: 
     data = sys.stdin.readline() 
     sock.sendto(data, target) 

def recv_msg(sock): 
    while True: 
     data, addr = sock.recvfrom(1024) 
     sys.stdout.write(data) 

Thread(target=send_msg, args=(sock_send,)).start() 
Thread(target=recv_msg, args=(sockfd,)).start() 
+0

不要忘記你的'從線程導入*' – Luke