PV0初めましてストリーム型のソケットプログラミングにおいて、送受信するアプリケーションデータの取り扱いについて、ご教示いただきたいことがございます。ストリーム型ソケットを使用する場合、各データは区切りなく送受信されるため、データにヘッダーを設け、そのデータ長を記載して送受信するかと思います。(データの最後に区切りコードを付ける方法もありますが、データ長を記載する方法で考えています。)送信側で、送信するデータのデータ長(Length)が正しく設定されていれば問題はないかと思いますが、送信側で誤って、実際に送信するデータサイズと異なるデータ長(Length)を設定した場合に、受信側でどのように扱えばよいか苦慮しています。次のデータのヘッダー部分を正しく受信することができませんし、また、大きな値が設定された場合は、次のデータのヘッダー部分まで受信してしまい、いずれにしても次のデータを適切に送受信できなくなってしまいます。一般的に、ヘッダーに設定されているデータ長(Length)が正しく設定されていないデータを受信した場合、次のデータを適切に扱うため、どのように判断すればよいのかお知恵を拝借いたしたくご質問させていただきました。更新順0なるほど、わかりました。ちなみに誤解なきように付け加えておきますがTCPの方が優れていると言っているつもりはありません。信頼性が高いというだけですね。(動画で過ぎたフレームが再送されても...とか書いていたんですが、長かったので消してしまってたのでTCP推しに見えてしまいましたね。)たしかにUDPは送信と受信が1対1になりますが、再構築されたパケットロスや順番が違っていて例えばデータ長の部分がおかしくなっている可能性もありますよね。(元々は送信データが間違っている可能性が...という話でしたが、UDPだと通信内容がおかしいリスクもありますね。詳しい様ですので説明する必要も無さそうですが。)TCPではたしかに送信と受信は1対1になりません。そこはたしかに考慮漏れてました。区切りを明確にしたいのであればソケットを閉じればいいとも思っていました。「Webページ」を構成するすべての情報(HTML、CSS、js、画像)を取得するにあたってはブラウザは複数のコネクションを同時に接続して取得します。各ファイルごとにソケットは閉じて次のリソースのために再度接続からやりなおします。これであればファイルという塊をきちんと区別して取得する事ができますね。(各リソースのHTTPヘッダとボディの間にCRLFという区切り文字があることはもちろん知っています。いう意味ではHTTPでは区切り文字も使い、レスポンスボディの長さ(ContentLength)もセットされていて、ファイルの区別はソケットを使い回さないことで(ソケットを分ける事で)データを区別するので、固定長以外の簡易な手法は一通りでてきてるかもしれません)ソケットの使い回しを考えなければUDPのデータグラムの制限よりも大きなデータは送信可能ですし、送りたいだけ送ってしまえばよく、明確に分けたいデータを送る時が来たらそれは別のソケットを使う...とすれば塊は表現できるとおもいます。(今回のコードでは明確な終了データらしきものを送信させましたが、TCPで使い回しを考えないケースではrecvを呼びつづけて、最初にrecv長が0になったらデータが最後まで来た(FINの後である)としてソケットを閉じるケースが多いと思います)塊はそれで表現できますからセットされているデータ長が正しいかどうかも比較できますので元の質問にあったような設定されているデータ長が誤っている場合も対処できます。(実際HTTPはContentLengthが正しいかどうかチェックされることがありますね)クロスチェックとしてチェックデータつけたい場合も固定長なら最後でも平気そうですね。(最初はWebSocketのフレームとか別の事と思ってましたもので...)実際にはデータの設計などが決まってから適切なものを選択する事になると思います(どれが正しいという事はないと思います)が、参考になれば幸いです。(0)貴重なノウハウをご教示(公開)いただき、ありがとうございました。ご教示いただきました事項を参考にさせていただきます。13:310誤解があったようですので、ご説明させていただきます。ご存じかと思いますが、UDPはその名のとおり、データグラム単位で送受信を行うプロトコルです。これに対して、TCPはバイトストーム型で、バイト単位で送受信を行うプロトコルです。UDPではデータグラム単位(意味をなすデータの塊)で送受信することができます。「AAAA」、「BBBB」という2つの意味をなす塊として、それぞれ受信することができます。これに対してTCPでは意味をなす塊であるデータグラムという単位は意識されず、単にバイト列として扱われます。受信側でも同様に単にバイト列として扱われます。例を挙げますと、アプリケーションが「AAAA」と「BBBB」という2つの意味をなす塊であるデータグラムをTCPを使用して2回にわたり送信した場合、受信側では1回の受信で「AAAABBBB」という単位で受信する場合があり、また、3回の受信で「AAA」、「ABB」、「BB」と単位で受信する場合もあります。単なるバイト列として送受信され、送信回数と受信回数には何ら関係はないのです。意味をなす塊で2回送信したので、2回受信すれば、意味をなす塊でそれぞれ受信できるとは言えないのです。TCPでは、「AAAA」、「BBBB」という本来意味をなす塊(データグラム)に関する情報はなく、このため、受信側アプリケーションでは受信されたバイト列を意味をなす塊(データグラム)に分ける必要があります。TCP、UDPともに下位のレイヤーとしてIPを使用しますので、IP層でのフラグメント化やフラグメント化されたパケットの再構築については基本的には同様です。(少し荒っぽいですが。)送信側では意味のある塊が意識できているので問題はないのですが、受信側で意味のある塊として受信できるかが異なります。これは、受信側でどのような状態で受信されるかということかと思います。実装上のお話になりますが、UDPでは各ソケットは受信バッファを持ち、ソケットに対して到着した「データグラム」はそのソケットの受信バッファに格納され、プロセスがrecvfrom(C言語)を呼び出すと、このバッファからFIFO順序で次のデータグラム(意味をなす塊)が返されます。TCPでも同様に各ソケットは受信バッファを持ち、ソケットに対して到着したデータがそのソケットの受信バッファに格納されます。プロセスがrecv(C言語)を呼び出すとその時点で受信バッファに格納されているバイト列が返されます。意味をなす塊では返されません。長文になってしましましたが、以下の文言の意味するところをご理解いただけましたでしょうか。UDPを使用して送受信してしたメッセージの塊をTCPを使用して送受信した場合、その塊の区切りは判断できないと思っています。ご意見をいただきました以下のような発想でもありません。通常、TCP(STREAM)の方が信頼がおける通信であり、UDP(DGRAM)の方が整合性を失いやすい通信です。UDPだと可能なものがTCPだと無理だと思われたのでしょうか(普通は逆ですね)UDP、TCPはそれぞれ固有の特性をもっています。それぞれの特性に応じた使い方をすれば良いと思っています。今日のインターネットの基盤を支えるDNSへの問合せでは、ご存じのとおりUDPが使用されています。TCPがUDPよりも優れているという発想もありません。TCPを使用する際は、バイトストリーム型であるため、その中から意味のある塊を適切に取り出す、あるいは不適切なものを排除する適切な方法がないかと思いお知恵を拝借した次第です。以下、ご質問をいただいております回答になりましたでしょうか。今回、ご質問をさせていただいた主旨ですが、UDPでもフラグメンテーションはされますので、小分けでしか送れないという点ではありません。誤解を招いたのであれば申し訳ありませんでした。「UDPだと塊で送れて安心、TCPだと塊は送れない小分けでしか送れないからどうしよう」という発端であればそれがそもそも勘違いではないかと思うのですがどうでしょう。(0)丁寧な説明ありがとうございます。あらたに回答をおこしましたが、そこに書いた以上のことは出せないと思いますので一旦この辺りで締めさせてもらえればと思います。01:15最後までお付き合いをいただき、ありがとうございました。幅広い知識をお持ちのようですね。更なるご活躍をされることをお祈りいたします。STREAMのストリームですね、ちょっとストリームデータと勘違いしていました。いずれにせよ、以下の発言が気になります。UDPを使用して送受信してしたメッセージの塊をTCPを使用して送受信した場合、その塊の区切りは判断できないと思っています。通常、TCP(STREAM)の方が信頼がおける通信であり、UDP(DGRAM)の方が整合性を失いやすい通信です。簡単なUDPとTCPの実装を書くと以下の様になります。ソケットライブラリを使っても流れは大体同じになるかと思います。マジックコメントつけてないので試してみるときは全角文字をソースに含めないようにしてください。3系ならどの環境でも動くかと思います。説明のためにソース書いたんですが、書きながら質問者さんの疑問がなにか考えながら直していたらソースはあまり関係なくなってしまいました。無関係にはなってないですが、ソース後のコメントだけ読んでもらって結構です。2接続を許可するようにしています(そのためスレッドも利用しています)。2個作ってサーバーに接続しにいきます。このときサーバーでacceptした段階で各クライアント接続用のソケット(c_sock)が作成されます。2つ接続されるので、2つのc_sockが作成されそれぞれのクライアントの通信は区別されて通信できます。Client一方UDPではそういうことにはなりません。socket.SOCK_DGRAM)6行目以降は同じ。こちらは先にendを受け取った段階でUDPソケットをクローズするので、2つ目のendは受信されない(さらにUDPだから受信確認も接続確認もないので2つ目のendがロストしていてもだれもエラーを出さない)。UDPではサーバーは単純に待ち受けて飛んできたデータを順次処理していくだけです。TCPの場合はサーバーが起動していないとクライアントはエラーとなりますが、UDPの場合はサーバーが起動していなくてもデータを送信します。受信されたかどうかの確認もされません。いった基本部分はご存知かもしれませんが、UDPを使用して送受信してしたメッセージの塊をTCPを使用して送受信した場合、その塊の区切りは判断できないと思っています。いう点をみるところ、データグラム(つまり塊)を受け取るUDPに対してTCPは小刻みに受け取るじゃないかという話なんでしょうか。3byteの送信を行っています。これをsock1.sendall(b'a'*65536)とした場合何が起こると思いますか?TCPは正常に送信できます。UDPは...エラーとなります(正確には環境にもよりますが)。ソケットで送信されたメッセージが、内部のメッセージのバッファーまたはほかのネットワークの制限を超えています。データグラムの受信に使われるバッファーがデータグラムより小さく設定されています。いうエラーになります。UDPはデータグラムを送信します。イーサネットフレームのサイズなどIPフラグメンテーションが発生すれば分割しなければなりませんがそれはIPの仕事です。IPがデータグラムまでは戻してくれますがUDPから2回送信したデータの届いた順番が正しいのかはUDPもIPも知りません。16bit長からヘッダサイズを除いたサイズという限界があります。環境によってはフラグメンテーションが許されずそもそも大きなデータグラムが送れないことがあり、その場合はUDPは分割できないのでエラーになります。TCPの場合はストリーム通信で受取先で組み立てられるのでその制限はありません。数GBでも転送できます。それを小分けに送ってもとの数GBまで組み立てるまでがTCPの仕事です。塊を送れないのではなくて、塊をばらして送って塊に戻すところまでやってくれるのです。「UDPだと塊で送れて安心、TCPだと塊は送れない小分けでしか送れないからどうしよう」という発端であればそれがそもそも勘違いではないかと思うのですがどうでしょう。(0)ご質問に対する回答が長くなりましたので、回答として記載させていただきました。20:070はじめに、お時間を割いていただきRFCをご確認いただいたのでしょうか。心から感謝申し上げます。明確なご回答をいただき、誠にありがとうございます。TCP(RFC6613)では、以下の記載があります。time.上記を実装する場合、Lengthフィールドのみで、一つのTCPコネクションの中で、複数のメッセージを適切に扱わなければならいことになります。TCP(RFC6613)では、また、以下のような記載があります。packet・・・Lengthフィールドが誤っている場合は、適切に属性データが送られているか判断できないと考えています。TCP(RFC6613)に限ったことではなく、多くのメッセージフォーマットでLengthフィールドが使われていますが、一般的にLengthフィールドのみでどのようにメッセージを扱われているか、お知恵を拝借させていただきました。Packetを排除する方法があればと思った次第です。Packetを送ってこないとも限りませんので、当該レイヤーで可能限り対策ができないかと。非常に勉強になりました。ありがとうございました。(0)1一般にストリームの場合はヘッダのLengthフィールドでパケット境界を教えるか、パケットの区切りコードでパケット境界を示すかは、プロトコル設計者がどっちか片方を選択するんじゃないかと思います。いずれにせよ送信側は正しいものをセットすることがコミュニケーションの前提条件になると思います。checkを行ったほうがよいと思います。6613)の場合は、ヘッダのLengthフィールドを使用します。6614の下の箇所が参考になります。6613の下記の箇所が参考になります。Lengthフィールドが誤っていたような場合、パケット境界を厳密に決定することはできないと思いますが、あからさまな間違いは検出できると思います。「1個前の」パケットがmalformedで「次の」パケットが切り出せないときは、コネクションを切断すべきであるようです。tochange....RADIUSの場合は、ヘッダの固定長部分とAttributeのリストからなる可変長部分がありますが、AttributeはTLVの形式で、個々のAttributeがL(ength)を持つので、リストを舐めながらその和をとって固定長部分と足せば、パケット全体長がわかるはずと思います。それとヘッダ内のLengthを比較すれば、ヘッダとボディ部分とで辻褄が合っているかどうかを確認できるのではないかと思いますので、ヘッダ内のLengthの間違いはある程度は検出できそうな気がします。(0)ご返信が長くなりましたので、回答で記載させていただきました。01:530ご回答いただき、ありがとうございます。独自にパケットのフォーマットを規定できるのであればご教示いただきました方法等を考えることができるのですが区切りコードのないデータ長を使用するプロトコルの場合は一般的にどのように扱われるのでしょうか。以下のようになっています。任意のオクテットこのパケットのフォーマットでは、「Length」のみでパケット長に合わないパケットは破棄しなければなりません。(区切りコード等がありません。TCP」というRFCがあり、その中で「RADIUSパケットフォーマットに変更はない」と記載されています。SCTPを使用する方法があるかとは思いますが、TCPで扱うことができればと思っています。TCP」の仕組みは、例としてあげさせていただきました。TCP等のストリームソケットにおいて、区切りコードを使用せず、データ長(Length)のみを使用して、どのように単一パケットを取り出されているか、お知恵をお借りしたいと思い質問させていただきました。19:08ちなみに、UDPの1パケットは、TCPでも1パケットです。19:10パケットという表現が不適切でした。UDPを使用して送受信してしたメッセージの塊をTCPを使用して送受信した場合、その塊の区切りは判断できないと思っています。TCP」では、どのようにこれまでのRADIUSパケットを送受信しているか教えていただないでしょうか。区切りコードを追加しているのでしょうか。18:33追伸:区切りコードを付加することでメッセージの塊を判断できることは理解していますが、標準プロトコル等のメッセージフォーマットを変えることになるため、ご質問をさせていただきました。18:370「送信側でデータ長を間違える可能性がある」のであればヘッダが信用できない場合があるという事なので、区切りコードか何かをセットしないと難しいんじゃないでしょうか。データの前に固定サイズのチェックサムなどがあったとしても、データ長が誤りであったと気付けるだけでどこで区切れば次のデータの始まりかは判断でき無い様に思います(データの後だと同じ理由でチェック用データを取得できないかもしれません)。BASE64の様にデータ部には特定の文字しか含めずにそれ以外のコードが来た場合は少なくともデータではないと判断できるようにするとか、データ中には絶対に現れないバイトデータをヘッダの前か後(ヘッダが固定長なら後でも逆算できます)につけるなどの方が簡単ではないかと思います。信用できる部分、信用できないことがある部分、救いたい部分の範囲(再送が要求できればいいのか、再送は許されず既にもらったデータでなんとかリカバリーできなければいけないのかなど)も整理した方がいいかもしれません。(0)ご回答いただき、ありがとうございます。返信が長くなりましたので、回答として記載させていただきます。(0)ウォッチこの質問への回答やコメントをメールでお知らせします。