2013-07-16 90 views
26

當我嘗試接收大量數據時,它會被截斷,我必須按Enter才能獲取其餘數據。起初我能夠增加一點,但它仍然不會收到全部。正如你所看到的,我增加了conn.recv()上的緩衝區,但它仍然沒有獲得所有的數據。它在某個時刻切斷它。我必須在我的raw_input上按回車才能接收其餘的數據。無論如何,我可以一次獲得所有的數據嗎?這是代碼。Python Socket接收大量數據

port = 7777 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.bind(('0.0.0.0', port)) 
sock.listen(1) 
print ("Listening on port: "+str(port)) 
while 1: 
    conn, sock_addr = sock.accept() 
    print "accepted connection from", sock_addr 
    while 1: 
     command = raw_input('shell> ') 
      conn.send(command) 
       data = conn.recv(8000) 
       if not data: break 
       print data, 
    conn.close() 

回答

67

TCP/IP是基於流的協議,而不是基於消息的協議。無法保證每一個對方的呼叫都會導致一個recv()呼叫由另一個對等方接收到發送的確切數據 - 由於數據包分段,它可能會收到數據零食,分割多個recv()呼叫。

您需要在TCP之上定義自己的基於消息的協議,以便區分消息邊界。然後,要閱讀郵件,請繼續撥打recv(),直到您閱讀完整郵件或發生錯誤。

發送消息的一種簡單方法是以每個消息的長度作爲前綴。然後讀取一條消息,首先讀取長度,然後讀取那麼多字節。這裏是你將如何做到這一點:

def send_msg(sock, msg): 
    # Prefix each message with a 4-byte length (network byte order) 
    msg = struct.pack('>I', len(msg)) + msg 
    sock.sendall(msg) 

def recv_msg(sock): 
    # Read message length and unpack it into an integer 
    raw_msglen = recvall(sock, 4) 
    if not raw_msglen: 
     return None 
    msglen = struct.unpack('>I', raw_msglen)[0] 
    # Read the message data 
    return recvall(sock, msglen) 

def recvall(sock, n): 
    # Helper function to recv n bytes or return None if EOF is hit 
    data = b'' 
    while len(data) < n: 
     packet = sock.recv(n - len(data)) 
     if not packet: 
      return None 
     data += packet 
    return data 

然後你可以使用send_msgrecv_msg功能來發送和接收整個郵件,並且不會與包被拆分或合併在網絡級別的任何問題。

+0

真棒感謝這麼多。我很感激。 – user2585107

+0

我不確定我是否完全理解這一點。我明白應該發生什麼,但我似乎無法得到它。我越來越'例外:套接字EOF試圖recv 4個字節' 我使用以下內容:http://pastebin.com/raw.php?i=AvdN5RyW – user2585107

+0

@ user2585107:嘗試更新的版本,它使用'返回None'而不是在流結束時引發異常。 –

3

您可能需要調用conn.recv()多次接收的所有數據。由於TCP流不保留幀邊界(即,它們只能用作原始字節流,而不是結構化消息流),所以不能保證一次調用所有數據, 。

有關該問題的其他說明,請參見this answer

請注意,這意味着您需要知道何時收到所有數據。如果發送方總是發送8000個字節,則可以計算迄今爲止收到的字節數,並從8000中減去該字節數以知道還有多少字節可以接收;如果數據是可變大小的,還可以使用其他各種方法,例如讓發送者在發送消息之前發送多個字節頭,或者如果它正在發送的ASCII文本可以查找換行符或NUL字符。

10

你可以使用它作爲:data = recvall(sock)

def recvall(sock): 
    BUFF_SIZE = 4096 # 4 KiB 
    data = b'' 
    while True: 
     part = sock.recv(BUFF_SIZE) 
     data += part 
     if len(part) < BUFF_SIZE: 
      # either 0 or end of data 
      break 
    return data 
+3

這適用於檢測「文件結束」,但不適用於保持連接並檢測消息的結尾。只有在peeer關閉其部分插槽或至少將其關閉一半時才能達到「文件結束」。 – glglgl

+3

如果收到的字符串少於4096個字符,它將再次循環,並使用'sock.recv()'重新檢查更多數據。這會掛起,因爲沒有更多的數據進入。如果'part'的長度小於'RECV_BUFFER'的長度,那麼代碼可以安全地跳出循環。 – SomeGuyOnAComputer

+1

@SomeGuyOnAComputer,謝謝,修正。 – JadedTuna

0

修改亞當羅森菲爾德代碼:

import sys 


def send_msg(sock, msg): 
    size_of_package = sys.getsizeof(msg) 
    package = str(size_of_package)+":"+ msg #Create our package size,":",message 
    sock.sendall(package) 

def recv_msg(sock): 
    try: 
     header = sock.recv(2)#Magic, small number to begin with. 
     while ":" not in header: 
      header += sock.recv(2) #Keep looping, picking up two bytes each time 

     size_of_package, separator, message_fragment = header.partition(":") 
     message = sock.recv(int(size_of_package)) 
     full_message = message_fragment + message 
     return full_message 

    except OverflowError: 
     return "OverflowError." 
    except: 
     print "Unexpected error:", sys.exc_info()[0] 
     raise 

我會,但是,大量使用鼓勵的原始方法。

3

使用生成功能(我認爲更Python)的變化:

def recvall(sock, buffer_size=4096): 
    buf = sock.recv(buffer_size) 
    while buf: 
     yield buf 
     if len(buf) < buffer_size: break 
     buf = sock.recv(buffer_size) 
# ... 
with socket.create_connection((host, port)) as sock: 
    sock.sendall(command) 
    response = b''.join(recvall(sock)) 
+0

的端那一個不會出現工作,如果該響應是大於緩衝區的大小要小。 – Shadur

+0

@Shadur,這很有趣,當你嘗試時會發生什麼?你能分享一下代碼來重現這個問題嗎?正如所寫,'recvall'應該產生接收到的每個緩衝區的內容,而不管它的大小如何,只要它不是空的。 – yoniLavi

+1

通過添加的調試語句判斷,它吸入第一個塊中的整個響應,然後掛起,等待下一個塊。下面的'chunck'答案也有同樣的問題,我在第二次測試中修復了它,看看chunck的長度是否小於緩衝區大小。我會測試是否可以修復您的解決方案。 - 編輯:它。 – Shadur

3

接受的答案是好的,但它會隨着大文件-string很慢是不可變類,這意味着更多的對象是創建每次使用+符號時,使用list作爲堆棧結構會更有效率。

這應該更好地工作

while True: 
    chunck = s.recv(10000) 
    if not chunck: 
     break 
    fragments.append(chunck) 

print "".join(fragments) 
相關問題