TCP/IPでの全二重通信を実現するには?

解決


OKA  2008-02-22 23:12:11  No: 67623

環境:
開発環境:MS C++ Ver 6
クライアント:Windows2000以降のOS
サーバ:Windows Server 2003
通信方式:TCP/IP
接続方式:ポート番号は一つだけとし、送受信はその一つのポートのみで行う。

現在上記の環境で、非同期通信での送受信を行うモジュールを開発しました。
クライアント、及びサーバから同時にデータ(ファイル)を送信する際、

クライアントからサーバへ1ファイルを送信。
サーバからクライアントへ1ファイルを送信。
クライアントからサーバへ1ファイルを送信。
サーバからクライアントへ1ファイルを送信。
(以降繰返し)

上記の様な挙動を取ります。

この時、送るファイルサイズがソケットから読み込める最大サイズ(FIONREADで取得できる値)を超えた場合、
受信側はいくつかのデータに分割してデータを読み込みます。

クライアントからサーバへ1ファイルを送信。
(サーバは複数のデータを受信・連結してファイルに保存)
サーバからクライアントへ1ファイルを送信。
(クライアントは複数のデータを受信・連結してファイルに保存)
クライアントからサーバへ1ファイルを送信。
(サーバは複数のデータを受信・連結してファイルに保存)
サーバからクライアントへ1ファイルを送信。

上記の()内が実際の挙動となります。

上記挙動を確認した人間から、これは「全二重ではなく半二重である」と言われました。
具体的には、
「1ファイルずつのやりとり」ではなく、
「1送受信単位のデータのやりとり」を行わなくてはいけないそうです。

ただ、今用いている方法では、send は一度で全てのデータ(1ファイル単位)を送ってしまいますし、
現在のものに改良を加えただけでは、全二重方式になりそうには思えません。
インターネットを利用して、いろいろ調べてみましたが、
今のところ有用となる情報源も見つからない状態です。

そこでいくつか質問がございます。

※  制限項目
  ポートは一つだけ用いることとし、二つ以上のポートを用いることは出来ない。
  サーバとクライアントは1:1である。
  TCP/IP以外の通信方式は考慮しない。
  通信機器は全二重通信に対応している物とする。

質問内容:
1.全二重方式は、シングルスレッドのアプリケーションで実現可能であるか、もしくはマルチスレッドで作成する必要があるか。
2.send を用いた際に、1ファイル単位での送信となってしまうが、これを分割して送信する方法が提供されているか。

また、上記質問に回答する形でなくても、全二重通信に関するなんらかの有用な情報をお知りでありましたら、
教えて頂ければと思います。

当プログラムを作成する際に参考にしたHP:
http://yonex1.cis.ibaraki.ac.jp/Win32/lecture03.html

上記HPにて掲載されていたサンプルプログラムソース
http://yonex1.cis.ibaraki.ac.jp/Win32/src/chat.cpp


YuO  2008-02-22 23:39:50  No: 67624

ソケットによるTCP/IPというレベルで見た場合,送信路であるソケットは全二重通信対応になっています。
つまり,単純にソフトウェアの作りが半二重通信になっていることが問題です。
単純に,送り終わったら相手から送られてくるのを待つのではなく,待ちつつも送ればいいだけです。

> 1.全二重方式は、シングルスレッドのアプリケーションで実現可能であるか、もしくはマルチスレッドで作成する必要があるか。

シングルスレッドで可能。ただし,非同期通信を行う必要があります。

> 2.send を用いた際に、1ファイル単位での送信となってしまうが、これを分割して送信する方法が提供されているか。

1ファイル単位の送信であるのは,送信側プログラムの都合です。
sendで送るデータに,ファイルという単位は存在しません。


仲澤  2008-02-22 23:52:05  No: 67625

Ether Netの物理的原理は半二重通信です。アナログ電話(全二重)とは
違います。

全二重っぽく見せるには
  A.送信は(受信中でも)またされずいつでも行える。
  B.受信は(送信中でも)またされず即座に行われる。
が重要であるかと思うので
上記の条件を「あえて困難な」シングルスレッドでどのように
実装するか。が問題となってきます。メッセージでスライスし
ていくしか無いと思いますが、これは自分でスレッドのタイム
スライススケジューリングをやるようなものでお勧めしません。
車輪の再発明に時間を割くより、マルチスレッドにしたほうが
良いでしょう。


OKA  2008-02-22 23:57:11  No: 67626

>>YuO 様

早速の回答ありがとうございます。

>シングルスレッドで可能。ただし,非同期通信を行う必要があります。
今現在非同期での送受信を行っていますが、
何故か  送信メッセージ  だけは接続後にしか発行されず、仕方がないのでタイマイベントを用いて送信を行っています。

具体的には、

  WSAAsyncSelect(sock, hWnd, WM_SOCKET,  FD_WRITE | FD_READ | FD_CLOSE ) 

上記で  送信・受信・切断を非同期で動作するようにし、

  LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wP, LPARAM lP)
内、

  case WM_SOCKET:
    if(WSAGETSELECTERROR(lP)!=0) return 0L;
    switch(WSAGETSELECTEVENT(lP)){

上記で非同期処理メッセージを受け取り、

    case FD_READ:

でデータの受信を行っています。

    case FD_WRITE:                  //メッセージ受信

も一応用意してありますが、
connect での接続後に一度だけ発行されるだけです。

もしかしたら、送信イベントをタイマで行っているのが問題なのでしょうか。
(ちなみに今は1秒毎に行っています)
本来はメッセージ受信時に送信を行うべきなのかもしれませんが、
メッセージが来ないと永遠にデータを送信することが出来ず・・・。

>1ファイル単位の送信であるのは,送信側プログラムの都合です。
>sendで送るデータに,ファイルという単位は存在しません。
了解しました。
データを分割してsendを行う方法を検討してみたいと思います。


OKA  2008-02-22 23:59:50  No: 67627

>>仲澤 様

ご回答ありがとうございます。

>車輪の再発明に時間を割くより、マルチスレッドにしたほうが
>良いでしょう。

やはりマルチスレッドで作成するのが王道なのですね・・・。
インターネット上にそういった資料が見つからなかったもので、
シングルスレッドでやるものだとばかり思っておりました。

資料もなく困難な状況ですが、マルチスレッドでの作成を検討してみたいと思います。


OKA  2008-02-23 08:55:47  No: 67628

YuO 様の回答にもありましたが、再度質問させてください。

send でデータを送信する際、
send関数に 10Kbyte のデータを渡すと、
受信側はそのまま 10Kbyte で受信したり、2Kずつ5回に分けて受信したりと、
様々な挙動を示します。

そこで、データを送信する際、明示的に 1Kbyte ずつ 10回に分けて送信を行うような
ロジックを作成し、その様なsendを行うことは、
ソケット通信の手法として正しいことなのでしょうか。

色々と調べてみましたが、上記の様な「明示的な分割ロジック」を
使用しているソースが見あたらなかったものですから。

ヒントでもかまいません。
なにかご意見頂ければと思います。
よろしくお願いいたします。


  2008-02-23 09:29:45  No: 67629

正しくない。つーかその辺はソケット通信の基本なんで、ちゃんと本やサイトを読んで勉強しなされ。見つからないのは探し方が悪いだけ。

http://www.kt.rim.or.jp/~ksk/wskfaq-ja/
http://www.kt.rim.or.jp/~ksk/sock-faq/indexj.html


YuO  2008-02-23 09:35:38  No: 67630

> Ether Netの物理的原理は半二重通信です。

1000BASE-Tとか無視ですか?
そもそも話をしている層が違いますが。

>車輪の再発明に時間を割くより、マルチスレッドにしたほうが
>良いでしょう。

そのための非同期通信だと思いますが,マルチスレッドにする意義はあるのでしょうか。
単に送受信を行う目的だけのためにマルチスレッドにするのは効率が悪いだけだと思います。

> もしかしたら、送信イベントをタイマで行っているのが問題なのでしょうか。
> (ちなみに今は1秒毎に行っています)
> 本来はメッセージ受信時に送信を行うべきなのかもしれませんが、
> メッセージが来ないと永遠にデータを送信することが出来ず・・・。

一度来たら,WSAEWOULDBLOCKエラーになるまでsendは続けて行うことができます。
http://msdn2.microsoft.com/en-us/library/ms740149.aspx
単に,WSAEWOULDBLOCKになるまでsendしていない上にタイマで間隔を開けているために,バッファが溜まりきっていないのでしょう。

> そこで、データを送信する際、明示的に 1Kbyte ずつ 10回に分けて送信を行うような
> ロジックを作成し、その様なsendを行うことは、
> ソケット通信の手法として正しいことなのでしょうか。

無意味です。やるのは勝手ですが,それを行っても受信側が10KByte分1回で受け取る可能性は十分にあります。
また,サイズを最初に送る等の方法で末端を相手に知らせる必要があります。
# TCP/IP使うなら,パケットという考え方を捨てること。


通りすがり  2008-02-23 18:27:46  No: 67631

送信・受信毎にスレッド上げれば、良いだけじゃないの?
普通はそうすると思う。

TCP/IP以下でその他同期管理は解決してくれるし。

クライアント:(メインスレッド+受信待機スレッド)→送信・受信毎にスレッド立ち上げ
サーバ:(メインスレッド+受信待機スレッド)→受信時に更に受信用スレッドと送信用スレッドを立ち上げる。

両方とも似たようなプログラムになると思うし、俺なら両方同じプログラムにしちゃう。


仲澤  2008-02-25 19:26:48  No: 67632

YuOさんへ。おつかれさまです。
本システム上、YuOさんへの「回答」のように見えてしまいましたが、
自分が発言を書いている時点ではYuOさんの発言が掲載されておらず、
自分は最初の質問者であるOKAさんへの発言として書いております。
ツリー構造になっていないため、誤解されるのも無理からぬこととは
存じますが、なにとぞご了解いただけますようお願いします。
また、単線でのベース周波数の多重化については理解しております。
よろしくお願いいたします。


アラー  2008-02-26 01:17:37  No: 67633

質問がずれています(というか勉強不足)。
>単線でのベース周波数の多重化
なんて、TCP/IPを使用する上でなんの意味もありません。

TCP/IP、パケット、ソケット、PIDなどを「書籍」で調べてください。
書籍とネットでは情報量が全く違いますので、ネット上で調べてもたいした結果は出ませんよ。(VC++も同じです。)


アラー  2008-02-26 01:27:02  No: 67634

暇なんで最後に結論だけ。

・マルチスレッドにして、スレッド毎に送受信。
・無意味なので、1k毎の分割はしない。

以上。


OKA  2008-03-06 00:42:40  No: 67635

質問を投稿してからだいぶ日が経ってしまいましたが、
時間に余裕が出来たので、レスをお返ししたいと思います。

>>お 様
今回のソケット通信PG開発では、概念を正しく理解していなかったことにより、
ずいぶんと無駄な遠回りをしてしまったように感じます。

ある程度の知識は得たつもりですが、まだまだ完璧とは言えない状態ですので、
時間がある時に提示頂いた資料を基に、再度概念の理解から始めたいと思います。

>>Yuo 様
先方に Sendを複数に分けても、間に介在する通信機器によって更に分割される可能性
を説明したところ、納得して頂けました。
(今回はMTUが1500のルータが介在していたので、アナライザでパケットを調べたところ、約1400Byte程度に分割されておりました。
そのため、sendではサイズにかかわらず1回で送信することとなりました。

これも私がTCP/IPの概念を正しく理解していなかったことが原因だと思われます。

>>通りすがり 様
仰るとおり、今回は送信・受信を別のスレッドとし、
送受信は同期通信を行うように変更致しました。
(クローズ要求受付は非同期通信
アドバイスありがとうございました。

>> アラー 様
仰るとおりのプログラム変更となりました。
アドバイスありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加