複数のコネクションを操作するには?

解決


MSB  2005-02-15 02:29:03  No: 56400

VC++6.0にてチャットプログラムを作っています.
ソケットを使い1対1の通信まではできたので,複数の通信に移行したいのですがどうにもうまくいきません.
というかそもそも『ソケット』というものがよくわかっていないのかもしれません.
よかったらソケットを使った多人数通信のやり方を教えていただけませんか?
ちなみに通信部分はCAsyncSocketを元にTCPでやり取りしています.


シャノン  2005-02-15 07:17:33  No: 56401

ひとつのソケットは一つの相手としか通信できませんので、1対多の通信をやるには、1対1の通信をたくさんやることになります。
Accept を、接続したい相手の数だけ行って、接続したい相手の数だけソケットを生成してください。


MSB  2005-02-15 20:20:10  No: 56402

レスありがとうございます.
無知でまことに申し訳ないんですが,ソケットの生成というのは具体的にどこからのことを言うのでしょうか.
今回は
http://www.net24.ne.jp/~kenji/p_lantest/lantest.html
のソースを参考にさせていただいています.

ソケット生成に関する部分は主に以下の通りだと思うのですが

ダイアログのクラス(CServerDlg)
 BOOL CReversiDlg::OnInitDialog()
 {
          :
  m_pServerSock = new CServerSock(this);
  m_pServerSock->Open(theApp.m_Port);
          :
 }

CAsyncSocket派生クラス(CServerSock)
 BOOL CServerSock::Open(UINT Port)
 {
  m_Port = m_sock.m_Port = Port;
  if(!Create(m_Port))
  {
    TRACE("Create error %d\n",GetLastError());
    return FALSE;
  }
  if(!Listen())
  {
    TRACE("Listen error %d\n",GetLastError());
    return FALSE;
  }
  return TRUE;
 }

 void CServerSock::OnAccept(int nErrorCode) 
 {
  if(!nErrorCode)
  {
    if(!Accept(m_sock)) TRACE("Accept error %d\n",GetLastError());
    else                m_parent->PostMessage(WM_SERVER_ACCEPT,m_Port);
  }
  CAsyncSocket::OnAccept(nErrorCode);
 }

 // m_Port サーバ起動時に設定したポート
 // m_sock CServerSockのサブクラスのオブジェクト
 // m_parent CServerDlgのオブジェクト

この場合,CreateとListenはサーバが起動したとき(ダイアログが初期化されるとき)の一回でいいんでしょうか?


MSB  2005-02-15 22:12:13  No: 56403

いろいろなサイトを見ると,ソケット通信を電話でのやり取りに例えられているのを見かけます.
私の見解としては

TCP通信(サーバ側)
  Create  ソケット生成
          ソケット登録
  Listen  ソケット接続準備
  Accept  ソケット接続待機 
  Send    データ送信
  Receive データ受信
  Close   ソケット消去 

とすると,
Createで電話機を用意し,Listenで回線に繋ぎ,Accept(が呼ばれるタイミング)は呼び鈴が鳴っている状態だと思っています.
したがってソケットを1つ作って,あとはクライアントの接続要求の度にAcceptを呼び出してそのソケットにコネクションを追加する形になると思うんですが,どうでしょうか?

補足:参考にしたサイトには,通信する相手の数だけCServerSockのオブジェクトを作るという記述がありました.しかしこれだと1つのサーバで複数のポートを開けて通信をするという形になると思います.私の認識とはズレているのでこのあたりも含めて教えていただけると幸いです.
突っ込みどころ満載かもしれませんが,どうかよろしくお願いします.


シャノン  2005-02-15 22:26:48  No: 56404

まずは、ソケット通信モデルの基礎を理解されると良いと思います。
ソケット通信は Unix 系の BSD に端を発する、大変に歴史のあるモデルで、現在でもそれと互換性があるように作られていますので、C と Unix でやろうが MFC でやろうが C# でやろうが、基本はほとんど変わりません。

で、サンプルの中には CServerSock と CSubSock という2つのクラスがありますが、実際に通信を行うのは CSubSock の方です。
ソースを見るとわかると思いますが、CServerSock がやっているのは Create、Listen、Accept です。対して、CSubScok は Send と Receive をやっていますね。
CServerSock の役割は、「相手からの接続を待機して、相手が接続してきたら、その相手と1対1通信するための CSubSock を準備すること」だけです。
CServerSock 自体は、データの送受信を行いません。

このサンプルは、1対1通信しか意図していない(CSubSock を1個しか持っていない)ので、そのままでは1対多通信に用いることはできません。
1対多通信を行うには、CSubSock に相当するものを多数持ち、そのひとつひとつに1対1通信を行わせなければなりません。
CSubSock を準備するのは CServerSock の Accept なので、一般には、「Accept 専用スレッド」を作り、CServerSock はその中でひたすら Accept に専念するという方法になるのではないでしょうか。

#ただ、このサンプルは CServerSock 自体をいくつも持つことにより、1対多通信を行うというスタイルのようですが。


シャノン  2005-02-15 22:28:31  No: 56405

#レス書いている間に先回りされてしまいました

> したがってソケットを1つ作って,あとはクライアントの接続要求の度にAcceptを呼び出してそのソケットにコネクションを追加する形になると思うんですが,どうでしょうか?

そんな感じであっていると思います。


MSB  2005-02-16 02:10:14  No: 56406

丁寧な回答をありがとうございます.

>一般には、「Accept 専用スレッド」を作り、CServerSock はその中でひたすら Accept に専念するという方法になるのではないでしょうか。
>このサンプルは CServerSock 自体をいくつも持つことにより、1対多通信を行うというスタイルのようですが。

ということは,このサンプルを一般の形式で使用するには,CServerSock, CSubSock の作りを変えなくてはいけないということでよろしいでしょうか?
このまま使用するとしたら,ダイアログの

 BOOL CReversiDlg::OnInitDialog()
 {
          :
  m_pServerSock = new CServerSock(this);
  m_pServerSock->Open(theApp.m_Port);
          :
 }

の部分を複数回実行するわけですよね?
この場合のOpen()の引数になるポートは全て別になるのでしょうか?
とりあえず実行のテストは自分のPCのみで,クライアントはアドレスに“localhost”を指定しています.

今まで行ったことは

 BOOL CReversiDlg::OnInitDialog()
          :
  for(int i=0;i<10;i++){
    client[i].m_pServerSock = new CServerSock(this);
    client[i].m_pServerSock->Open(theApp.m_Port);
  }
          :
 }

として同じポートを使う(つもりで)実行してみました.
結果は,1つ目のクライアントの接続は正常にできるのですが,2つ目の接続でサーバがエラーを起こして落ちました.
次に

 BOOL CReversiDlg::OnInitDialog()
          :
  for(int i=0;i<10;i++){
    client[i].m_pServerSock = new CServerSock(this);
    client[i].m_pServerSock->Open(theApp.m_Port+i);
  }
          :
 }

とし,ポートを別にして接続させるつもりで書きました.
今度も1つ目の接続は成功.それ以降もどうやら通信は行われているようです.
今このページを見ている場所では確認が取れないので,確認が取れ次第書き込みたいと思います.
ただ,コネクション確立後名前の交換をするようにしているんですが,後者の場合それが正しく行われず,クライアントはサーバの名前を知らないまま通信が行われてました.


MSB  2005-02-16 02:13:01  No: 56407

追加です.
client[]はCServerSockのオブジェクトと,クライアントの名前,使用されているかどうかのフラグ(必要無いかもしれませんが)が入った構造体です.


MSB  2005-02-16 23:45:10  No: 56408

どうやら私は,ソケットというものに対してかなり間違った解釈をしていたようです.
Acceptという関数は,リスンソケットに接続してきたホストに対して新しいコネクションを作る関数だったんですね.
↑は大分見当違いなコトをしていたみたいです.

シャノンさんのおっしゃったとおり,スレッドを使ったモノに書き直したいと思います.
ありがとうございました.

## 同じような疑問をお持ちの方へ
言語は違いますが,ソケットを使った通信のやり方が一通り書いてあります.
  http://www.techscore.com/tech/Java/Network/2.html

ソケット通信の際のListenやAcceptの役割について話し合われてます.
ただし,“ソケット”についてではなくポート番号にのみ着眼している感じがして個人的には疑問符がつきます.
  http://forums.belution.com/ja/vc/000/088/08.shtml


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

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






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