Problem
我的python程式如下,目標是建立一個socket server等待連線,並將檔案給送到socket client:
import socket import os import sys import stat from os.path import dirname, abspath, join def transmitDebugInfo(): filename = "/bootpart.gz" debugfile = None s = None conn = None try: debugfile = open(filename,'rb') blocksize = os.path.getsize(filename) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.settimeout(60) s.bind(('0.0.0.0', 5555)) s.listen(1) conn, addr = s.accept() data = debugfile.read() conn.send(data) except: print sys.exc_info()[0] print sys.exc_info()[1] finally: if s is not None: s.close() if conn is not None: conn.close() if debugfile is not None: debugfile.close() transmitDebugInfo()
我的client為了方便測試,是直接使用linux的nc command:
nc 192.168.0.1 5555 > test.txt
然而,在VMWare ESXI6.0 u2下會出現以下錯誤:
<class `socket.error`> [Errno 11] Resource temporarilty unavaliable
Diagnosis
實驗1 - 分批送+sleep
首先我發現接收到的檔案大小一定是33304 bytes,於是我加了以下程式做試驗:
print "bufsize = %s" % s.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 5000) print "bufsize = %s" % s.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
server程式輸出為:
bufsize = 32768 bufsize = 5000
而client輸出檔案大小則變為5792 bytes。我猜想可能和發送buffer有關,於是找了一台ubuntu 14.04做測試;沒想到雖然buffer size為16384卻能正常的發送完檔案。因此我猜測可能和buffer釋放時間有關,於是改寫了以下發送方式:
while 1: data = debugfile.read(1024) if not data: break conn.send(data) time.sleep(0.000001)
以每次1024 bytes發送,每次中間等待0.001秒,如此就能順利完成發送。但問我無法確定是由於send data thread搶了所有執行資源,造成buffer無法清空的問題;還是由於清buffer就是比較慢。在無法確認實際原因情況下,這方法我不敢使用。
實驗2 - Remove settimeout
在remove settimeout後,就可以正常傳送與接收資料;沒設定timeout等效於setblocking(1)。
實驗3 - setdefaulttimeout
我在建立socket前,使用socket.setdefaulttimeout(60),發現可以正常傳送與接收資料。這挺奇怪的,我還不曉得這與settimeout有何不同。
實驗4 - 使用strace去跑分批送
與實驗1的區別在於,我移除sleep並透過strace去執行也可以正常執行完畢。
strace -o output.txt -s512 python test.py
我猜測透過strace可能也造成send資料之間的延遲,好讓buffer有足夠時間釋放。
How to resolve?
根據實驗結果,有以下結論:
- 實驗二說明在blocking mode下,sendall應能正常傳送資料。實驗三為例外,原因還不明。
- 實驗一與四說明,如果將send分批進行且有緩衝,應能正常傳送資料。
- 並非所有OS有相同結果,可能與python或內核有關。
綜合以上,如果要設定timeout,安全作法應該使用non-blocking IO的存取方式。針對Resource temporarilty unavaliable錯誤retry:
def sendData(sock, data): total_sent = 0 while len(data): try: sent = sock.send(data) total_sent += sent data = data[sent:] print 'Sending data' except socket.error, e: if e.errno != errno.EAGAIN: raise e print 'Blocking with', len(data), 'remaining' select.select([], [sock], []) return total_sent
呼叫程式改為:
data = debugfile.read() total_data = len(data) total_send = sendData(conn, data) print "data size = %s, send size = %s" % (total_data, total_send)
留言
張貼留言