質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

4033閲覧

Python Socketでrecv()で設定したサイズ以上のデータ受信について

ngh_orange

総合スコア17

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/11/19 07:36

前提・実現したいこと

Pythonでsocketを使い、チャット&ファイル送受信ソフトを作りたいです
チャット機能自体は作れたのですが、ファイル受信の機能がうまく作れません
受信側のrecv()で設定したサイズ以上のデータを送ると全て送れず、下記のコードで受信をループさせると今度は終わらせることができません。
どのようにすれば、データがすべて受信できたことを確認することができるでしょうか?

発生している問題・エラーメッセージ

python

1エラーメッセージ

該当のソースコード

python

1       while True: 2 rv1 = sock.recv(4096) 3 if type(rv) != bytes: 4 rv = b"" 5 bytes(rv) 6 if(rv1 == b""):break 7 rv += rv1 8 print(len(rv1)) 9 print(type(rv)) 10 rv = pickle.loads(rv) 11 print("rvの中身 :",rv) 12 print(type(rv)) 13

※インデントが少しおかしいかもしれません。

実装したいのは「データがもう送られてこなかったら」breakし、ループを抜けてそれをpickleでロードしたいです
こちらの質問内容から、「if not data(自分の場合はrv1)」はソケットがあるかどうかを監視していることがわかり。自分はチャットソフトとしても使うと想定しているので一回一回ソケットを閉じていられません。

どのようにすれば、データがすべて受信できたことを確認することができるでしょうか?

試したこと

似たようなteratailの質問
ループの基礎

補足情報(FW/ツールのバージョンなど)

Windows10
python 3.9.4

おかしなところがあれば申し訳ありません。色々教えていただければ幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

どのようにすれば、データがすべて受信できたことを確認することができるでしょうか?

送信側が事前にデータサイズを通知しておく必要があります。

一般的なバイナリーデータの送受信では、
ヘッダー部分(固定長のデータ) + ペイロード部分(可変長のデータ)
として、パケット(ヘッダ+ペイロード) 単位でやり取りをします。

python

1import struct 2from types import SimpleNamespace 3 4def _read_length(stream) -> int: 5 assert struct.calcsize(">I") == 4 6 return struct.unpack(">I", stream.read(4))[0] 7 8def _read_payload(stream, length) -> bytes: 9 return stream.read(length) 10 11def read_packet(stream): 12 length = _read_header(stream) 13 payload = _read_payload(stream, length ) 14 obj = pickle.loads(payload) 15 return SimpleNamespace(**locals()) 16 17def send_packet(sock, obj): 18 payload = pickle.dumps(obj) 19 length = len(payload) 20 sock.sendall(struct.pack(">I", length) 21 sock.sendall(payload) 22 23 24... # 略 25stream = sock.makefile("rb") 26packet = read_packet(stream) 27print(packet.obj)
  • 要点を解りやすくする為、送受信はブロッキング (read/sendall) で記載。

recv/send の場合は、追加で独自にバッファ管理をする必要があります。

  • エラー処理は省いてますが、エンディアンの違いでも

 巨大なサイズのペイロードを読み取ろうとしてしまうので注意。
実際のアプリケーションでは、ヘッダ部分に様々なデータを含めます。


追記: pickle化するデータを送受信する例
ネットワーク越しの logging イベントの送受信

投稿2021/11/21 05:01

編集2021/11/21 23:03
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

len(rv1)が4096未満になるまで繰り返せばよいと思います。

投稿2021/11/19 08:07

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

teamikl

2021/11/21 04:10

recv に指定するのはバッファの最大値なので、読み込まれる実際のデータサイズは保証されてません。 ローカル環境通しのテストでは意図通りの動作となるかもしれませんが、実際の環境では問題です。 socketからmakefileで作成したfile-like オブジェクトのread なら、想定されてるような挙動 指定バイト読み込むまで待機します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問