2014-03-12 98 views
2

我想要簡單的腳本客戶端套接字。我的代碼如下:爲什麼我的客戶端套接字在首次發送後死亡?

import pickle 
import socket 

pole = ["mail","firewall","fria"] 

c=False 
while c==False: 
    try: 
    client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
    client.connect (('localhost', 1234)) 
    print("established") 
    c=True 
    except: 
    print(".") 

i = (len(pole)-1) 
while i>=0: 
    client.send(pickle.dumps(pole[i])) 
    pole.remove(pole[i]) 
    i-=1 

client.close() 

另一方面是「永遠」的服務器。但服務器只收到一塊數據。爲什麼沒有所有的領域?客戶端「,而」循環應該運行3次,所以它應該發送所有的([「郵件」,「防火牆」,「弗里亞」))。這裏是服務器的輸出:

enter image description here

然後客戶端結束。爲什麼?客戶應在發送所有數據後結束。爲什麼在一個發送連接關閉後?

感謝您的幫助

編輯---> server.py:

import pickle 
import socket 

updated_data = []   
server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
server.bind (('', 1234)) 
server.listen (5) 

while 1: 
    channel, details = server.accept() 
    print ('Received connection:', details [ 0 ]) 
    updated_data.append(pickle.loads(channel.recv(1024))) 
    print (updated_data) 

channel.close() 
+0

您的1個send()調用很可能僅由1個recv()調用讀取。請記住,TCP是一個流協議,它不知道或保留任何消息邊界。 – nos

+0

永遠不要在網絡協議中使用泡菜。閱讀http://docs.python.org/2/library/pickle.html上的大紅色框。 –

回答

4

我只是測試你的代碼,並透露正在由服務器接收到的所有信息,你從環send()作爲一個單一的和pickle只加載其中的第一個。基本上,所接收的消息是

b'\x80\x03X\x04\x00\x00\x00friaq\x00.\x80\x03X\x08\x00\x00\x00firewallq\x00.\x80\x03X\x04\x00\x00\x00mailq\x00.' 

正如你可以看到,所有的pole元件是在該串 - 這意味着客戶端發送所有數據的連接關閉,服務器從客戶端接收的所有數據之前。

解決方案是框架(設置消息的邊界)消息。這個問題比較廣泛(谷歌搜索結果爲tcp message boundarytcp message framing)。

我覺得this blog post非常有幫助。它說:

有兩種常用於消息成幀的方法:長度爲 前綴和分隔符。

長度前綴前置每條消息的長度爲 消息。長度前綴的格式(和長度)必須明確說明爲 ; 「4字節有符號小端」(即C#中的「int」) 是常用選項。爲了發送消息,發送方首先將該消息轉換爲字節數組,然後發送數組字節 ,接着是字節數組本身的長度。

由於部分接收的可能性,接收長度前綴的消息比較困難。首先,必須將消息的長度 讀入緩衝區,直到緩衝區滿(例如,如果使用 「4字節有符號的小端」,該緩衝區爲4字節)。然後一個 分配第二個緩衝區並將數據讀入該緩衝區。當 第二個緩衝區已滿時,則單個消息已到達,並且一個 回到讀取下一個消息的長度。

定界符比較複雜,無法正確定位。發送時,數據中的任何分隔符 必須替換,通常使用轉義函數 。接收代碼無法預測傳入消息的大小,所以它必須將所有接收到的數據附加到接收緩衝區的末尾,並根據需要增大緩衝區。當發現一個分隔符時,接收端可以在接收端緩衝區中應用一個無法使用的功能來獲取該消息。如果這些消息永遠不會包含 分隔符,那麼可以跳過轉義/消除函數。

在您的特定情況下,我會去用Python的list(將作爲「自動分隔容器」),系列化與pickle,所以我不得不改變你的代碼位。

客戶

import pickle 
import socket 

pole = ["mail","firewall","fria"] 

c = False 
while not c: 
    try: 
     client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
     client.connect (('localhost', 1234)) 
     print("established") 
     c = True 
    except: 
     print(".") 

# - i = (len(pole)-1) 
# - import time 
# - while i>=0: 
# -  client.send(pickle.dumps(pole[i])) 
# -  pole.remove(pole[i]) 
# -  i-=1 

# + 
client.send(pickle.dumps(pole)) 

client.close() 

服務器

import pickle 
import socket 

updated_data = []  
server = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
server.bind (('', 1234)) 
server.listen (5) 

while 1: 
    channel, details = server.accept() 
    print ('Received connection:', details[0]) 
    data = channel.recv(1024) 
    if data: 
     # Deserialize 
     data = pickle.loads(data) 
     # We're able to extend the list with another list: 
     # [1, 2, 3].extend([4, 5, 6]) is [1, 2, 3, 4, 5, 6] 
     updated_data.extend(data) 
     print(updated_data) 

channel.close() 

現在代碼工作和服務器的打印

Received connection: 127.0.0.1 
['mail', 'firewall', 'fria'] 

updated_data增長預期。

+0

感謝您的建議,但我的意思是第二次while循環,它應該運行三次。第一個循環只建立連接。如果c爲真,則意味着連接被接受。然後客戶端可以發送數據.. – Joffo

+0

@Joffo更新了一些細節 – vaultah

0

我只取得了如下修改客戶端:

i = (len(pole)-1) 
while i>=0: 
    c=False 
    while c==False: 
     try: 
     client = socket.socket (socket.AF_INET, socket.SOCK_STREAM) 
     client.connect (('localhost', 1234)) 
     print("established") 
     c=True 
     except: 
     print(".") 

    client.send(pickle.dumps(pole[i])) 
    pole.remove(pole[i]) 
    i-=1 

但在這個解決方案,我需要三次esteblish連接。有沒有辦法只建立一個,併發送所有數據?

+1

這沒有回答這個問題。如果您需要共享其他信息,請編輯您的問題。 –

2

我認爲所有三個轉儲發送的第一次,只有一個被加載。

我在while循環之外移動了服務器accept(),並在客戶端的發送循環中放置了一個睡眠(0.01)。 updated_data按預期增長。

這是一次性解決方案,因爲服務器只接受一個連接。

相關問題