2016-08-10 15 views
2

クライアントがサーバーに接続すると、ディレクトリを参照してファイルを取り込むことができるPythonプログラムを作成しようとしています。ブラウジング部分はうまく動作し、クライアントのすべてのディレクトリを出力します。 は、ここでは、コードの一部です:ソケットPython 3.5:ソケットサーバーが永久にファイルを受信すると受信する

with clientsocket: 
    print('Connected to: ', addr) 
    while True: 
     m = input("Command > ") 

     clientsocket.send(m.encode('utf-8')) 
     data = clientsocket.recv(10000) 

     if m == "exit": 
      clientsocket.close() 
     if m.split()[0] == 'get': 
      inp = input("Filename > ") 
      while True: 
       rbuf = clientsocket.recv(8192)     
       if not rbuf: 
        break 

       d = open(inp, "ab") 
       d.write(rbuf) 
       d.close() 


     elif data.decode('utf-8').split()[0] == "LIST": 
      print(data.decode('utf-8')) 
     if not data: 
      break 

しかし、問題はここにある:

if m.split()[0] == 'get': 
    inp = input("Filename > ") 
    while True: 
     rbuf = clientsocket.recv(8192)     
     if not rbuf: 
      break 

無限ループに陥っているように見えます。もっと興味深いのは、私が受け取ろうとしているファイルは88.3kbですが、ファイルが返すものはループに入っている間に非常に近いものです...

一度にpythonスクリプトを受け取ろうとしました同様に(ループなし)、それは正常に動作します。

は、ここでクライアントコードの一部です:

while True: 
    msg = s.recv(1024).decode('utf-8') 


    if msg.split()[0] == "list": 
     dirs = os.listdir(msg.split()[1]) 
     string = '' 
     for dira in dirs: 
      string += "LIST " + dira + "\n" 
     s.send(string.encode('utf-8')) 
    elif msg == "exit": 
     break 
    else: 
     #bit that sends the file 
     with open(msg.split()[1], 'rb') as r: 
      s.sendall(r.read()) 

私はそれはデータがないときに閉じるように設定し、どのように私はできているのであれば、私の質問は、なぜそれが無限ループで立ち往生されていますこれを修正しますか?

私は一般的にネットワークプログラミングに新しいので、明らかに何かが間違っていたら私を許してください。

ありがとうございます!

+0

try ... except ... ??? – chapelo

+0

これは以前のデバッグ用で、もう必要ないと思います。 – Megalegacy

答えて

1

私は問題が何であるか知っていると思いますが、間違っている可能性があります。正しい長さを指定したとしても、1つのrecvコールでメッセージ全体が受信されないことが何度か起こりました。しかし、ストリームの最後には到達しないので、プログラムは決して到着しない8192バイトの残りを待っています。
送信ファイル:ファイルを受信

#bit that sends the file 
with open(msg.split()[1], 'rb') as r: 
    data = r.read() 
    # check data length in bytes and send it to client 
    data_length = len(data) 
    s.send(data_length.to_bytes(4, 'big')) 
    s.send(data) 
s.shutdown(socket.SHUT_RDWR) 
s.close() 

# check expected message length 
remaining = int.from_bytes(clientsocket.recv(4), 'big') 
d = open(inp, "wb") 
while remaining: 
    # until there are bytes left... 
    # fetch remaining bytes or 4094 (whatever smaller) 
    rbuf = clientsocket.recv(min(remaining, 4096)) 
    remaining -= len(rbuf) 
    # write to file 
    d.write(rbuf) 
d.close() 
+0

'remaining = int.from_bytes(clientsocket.recv(4)、 'big')'はどのように動作しますか? – Megalegacy

+0

'clientsocket.recv(4)'データ長をエンコードする最初の4バイトを受信します。 バイトは、エンディアンをバイトにエンコードするために使用したビッグエンディアンのバイトオーダーを使用してintに変換されます。 – warownia1

+0

あなたの提案は機能しました!ありがとう。 – Megalegacy

1

はあなたのコードにはいくつかの問題があります

はこれを試してみてください。

まず:

clientsocket.send(m.encode('utf-8')) 
data = clientsocket.recv(10000) 

これは、あなたがget文を発行するとき、ファイルの一部がdata変数にロードされます。だからあなたは完全なファイルを取得しません。

今、この:

while True: 
    rbuf = clientsocket.recv(8192)     
    if not rbuf: 
     break 
    ... 

あなたは確かに完全なファイルをロードしますが、クライアントが接続を閉じることはありません(それはファイルを送信した後、s.recv()になる)ので、ifの文が満足になることはありません。したがって、このループはファイルをダウンロードした後にclientsocket.recv(8192)部分でブロックされます。

したがって、接続がまだ開いていても、すべてのデータを送信したことを何らかの方法でダウンローダに通知する必要があるという問題があります。それを行うにはいくつかの方法があります:

  1. ファイルのサイズを計算し、最初の数バイトとして送信します。たとえば、ファイルの内容がala ma kotaであるとします。これらは11バイトなので、\x11ala ma kotaを送信します。受信者は最初のバイトがサイズであることを知っています。もちろん、1バイトのヘッダーはそれほど多くはありません(最大256バイトのファイルしか送信できません)ので、通常は4バイトなどです。クライアントとサーバー間のプロトコルは次のようになります。最初の4バイトはファイルのサイズです。したがって、最初のファイルは\x0\x0\x0\x11ala ma kotaとして送信されます。このソリューションの欠点は、コンテンツを送信する前にコンテンツのサイズを知る必要があることです。

  2. ストリームの最後にマークを付けます。だから、特定のキャラクターを選択すると、Xとなり、Xが見つかるまで、あなたはストレインを読んでいます。あなたがそうするなら、相手側が持っているすべてのものを送ったことを知っている。欠点は、Xがコンテンツに含まれている場合、エスケープする必要があることです。つまり、追加のコンテンツ処理(および反対側の解釈)が必要です。

関連する問題