2013-11-15 22 views
1

我是套接字編程和python的初學者。我想了解如何從服務器向客戶端發送大型文本文件(例如> 5MB)。我不斷收到一個錯誤,說如何讀取大文件(套接字編程和python)?

Traceback (most recent call last): 
    File "fserver.py", line 50, in <module> 
    reply = f.read() 
ValueError: Mixing iteration and read methods would lose data 

下面是我的部分代碼。有人可以看看,並給我一些關於如何解決這個問題的提示嗎?感謝您的時間。

myserver.py

#validate filename 
     if os.path.exists(filename): 
      with open(filename) as f: 
       for line in f: 
        reply = f.read() 
        client.send(reply) 
      #f = open(filename, 'r') 
      #reply = f.read() 
      #client.send(piece) 
     else: 
      reply = 'File not found' 
      client.send(reply) 

myclient.py

while True: 
    print 'Enter a command: list or get <filename>' 
    command = raw_input() 
    if command.strip() == 'quit': 
     break 
    client_socket.send(command) 

    data = client_socket.recv(socksize) 
    print data 
+0

一旦你解決這個問題,也有一些其他的問題,你的碼。你只在客戶端上做一個'recv',這不太可能得到一個完整的文件。而且,即使這樣做,你也無法知道這是否是整個文件。你可能需要一個稍微複雜一些的協議,首先發送一個長度,然後客戶端一直調用'recv'直到獲得與該長度一樣多的字節。此外,客戶端需要一些方法來區分錯誤,如「找不到文件」,以及實際的文件內容。你需要在服務器中調用'sendall',而不是'send'。 – abarnert

回答

4

這裏的問題無關用插座,或與文件有多大。當你這樣做:

for line in f: 
    reply = f.read() 

for line in f試圖在同一時間讀取該文件的一行,然後每行你想讀取整個文件。這是行不通的。

如果你沒有得到這個錯誤(你不會在很多情況下),第一次通過循環,你會閱讀並忽略第一行,然後讀取併發送除第一行(或,可能除了第一個,比如說4KB)作爲一個巨大的回覆,然後循環完成。

你需要的是一個或另一個:

for line in f: 
    reply = line 

......或者......

# no for loop 
reply = f.read() 

同時,在你的客戶端,你只是做一個recv。這將會得到第一個4K(或其他socksize)或更少,然後你再也沒有收到任何東西。

你需要的是一個循環。像這樣:

while True: 
    data = client_socket.recv(socksize) 
    print data 

但現在你有一個新的問題。一旦文件完成,客戶端將永遠等待下一塊數據,永遠不會到來。所以客戶需要知道何時完成。唯一可以知道的方式是服務器將該信息放入數據流中。

這樣做的一種方法是發送文件前的長度。一種標準化的方法是使用netstring協議。你可以找到爲你做這件事的圖書館,但它很容易手工完成。或者也許做一些更像HTTP的事情,頭文件之間用換行符分開,並用空行分隔開來;那麼你可以使用socket.makefile作爲你的協議實現。甚至是一個二進制協議,您只需將這個長度作爲四個字節發送即可。

還有一個問題,我們可能會修復,而我們在這裏:send(reply)不一定會發送整個回覆;它發送從1字節到整個事物的任何地方,並返回一個數字,告訴你發送了什麼。這個簡單的解決方法是使用sendall(reply),它保證發送它的全部內容。

最後:您的服務器預計每個recv都會得到一個命令,如send發送的那樣。但套接字不這樣工作。 Sockets are byte streams, not message streams;沒有什麼能夠阻止recv獲得,比如只有一半的命令,然後你的服務器就會中斷。所以,你在這個方向也需要某種協議。再次,您可以使用netstring,或換行符分隔的郵件,或二進制長度前綴,但您必須執行

(以上鍊接的博客帖子有使用二進制長度前綴作爲協議非常簡單的示例代碼。)

+0

謝謝你的提示!我開始沒有循環,但我只能得到1024字節的數據。我嘗試了你給出的第一個建議,我的程序編譯了,但現在我遇到了一個不同的問題。我的客戶不打印整個文件。你能指點我正確的方向,讓我知道我失蹤了什麼?我想發送一個大文本文件到客戶端,客戶端可以在他的控制檯上打印文件。 – user2203774

+0

@ user2203774:我對這個問題的評論給了你一些提示。我會更新答案來詳細說明。雖然也許這應該是一個單獨的問題,而不是在這裏跟進?如果編輯後仍然沒有看到,請考慮創建一個新問題。 – abarnert

+0

非常感謝你的解釋! =) – user2203774

0

你可以做for line in file.readlines()

+0

(a)沒有以任何方式解決他的問題,(b)是不好的建議。 '對於文件行'來做同樣的事情,除了它不會嘗試將整個文件讀入內存並在它開始循環之前將其解析爲行。 – abarnert