マルチスレッドのサーバから,クライアントに返信するには?

解決


MSB  2005-02-21 08:18:16  No: 56485

http://www.net24.ne.jp/~kenji/p_lantest/lantest.html
http://x68000.q-e-d.net/~68user/net/echo-3.html
を参考にVC++6.0でチャットプログラムを作っています.
現在サーバをマルチスレッド化し,複数のクライアントの接続を処理し,送られてきたデータをそのまま返信するまではできました.
しかしそのデータを他のユーザに送るやり方がわかりません.
よろしかったらご教授願いたいです.

プログラムの概要としては以下の通りです.
*(クラス名):このクラスの型の変数

class CServerDlg    :CDialog      :メインのダイアログ
class CServerSock   :CAsyncSocket :接続要求を処理
class CServerThread :CWinThread   :実行されるスレッド
class CSubSock      :CSocket      :データの送受信

CServerDlg::OnInitDialog
  (CServerSock).Create(〜)を呼び出す
  (CServerSock).Listen()を呼び出す

CServerSock::OnAccept
  Accept(仮のソケット)で接続要求を処理
  (CServerThread*)=AfxBeginThread(〜)実行
  スレッドに,Acceptで指定したソケットを引き渡す
  スレッド開始(再開?)

CServerThread::InitInstance
  (CSubSock).Attach(引き渡されたソケット)

CSubSock::OnReceive
  CSocketFile file(this);
  CArchive In(&file, CArchive::load);
  CArchive Out(&file, CArchive::store);

  Inからデータ読み込み
  Outへデータ書き込み(送信)
  PostMessageにてメインのダイアログにデータを投げる

となっております.
CSubSock::OnReceive内で送受信を処理してしまっているので他のスレッドには送られません.
PostMessageで投げたデータはメインのダイアログに表示されておしまいです.


NowNow  2005-02-22 03:40:18  No: 56486

>   (CServerThread*)=AfxBeginThread(〜)実行
とあるので、CServerThread*をどこかへとっておいて、メッセージがきたら(CSubSock::OnReceiveで)すべてのソケットスレッドへ対して送信するような仕組み(すべてのCServerThreadへ送信するメッセージをポストしたり直接送信してしまうなど)を、作ればいいのではないでしょうか?


MSB  2005-02-22 11:09:23  No: 56487

お返事ありがとうございます
私もそれでできると思い,少々手を加えてみたのですがうまく行きませんでした.
とりあえず基本に戻り1対1の通信のみをスレッドで行うつもりで以下のような変更をしたのですが

class CServerDlg :
  CServerThread* thread; //とりあえずスレッドへのポインタを1個用意

//// 追加 ////
LONG CServerDlg::OnNewString(WPARAM, LPARAM)
{
  thread.SendString(〜);

void CSubSock::OnReceive(int nErrorCode) 
{
  CString str;
  CSocketFile file(this);
  CArchive arIn(&file, CArchive::load);
//  CArchive arOut(&file, CArchive::store); //除外

  do
  {
    arIn >> str;
    str.MakeReverse();
//    arOut << str; //除外
  }while(!arIn.IsBufferEmpty());
  str.MakeReverse();
  m_pCriticalSection->Lock();
  *m_pLastString = str;
  ::PostMessage(AfxGetApp()->m_pMainWnd->m_hWnd, WM_RECEIVE, 0,0);
  
  m_pCriticalSection->Unlock();

  CSocket::OnReceive(nErrorCode);
}

//// 追加した関数 ////
void CSubSock::SendStirng(CString cs)
{
  CSocketFile file(this);
  CArchive arOut(&file, CArchive::store);
  arOut << cs;
}

実行したところ,接続自体はできるのですが,サーバはデータが送られてくるとエラーを起こして落ちてしまいます.
単なる記述ミスなのでしょうか?大変自分勝手ではありますがよろしくお願いします.


NowNow  2005-02-22 19:12:26  No: 56488

ソケットプログラミングはあまり深くやったことがないので、間違いがあるかもしれませんがそれを前提に…。

エラーで落ちるとのことですが、どの部分で落ちますか?
strへデータを読み込み部分か、PostMessageの部分か、SendStirngか、もしくはCServerDlgのメンバー関数かなど。

直接関係ないかもしれませんが、
>    m_pCriticalSection->Lock();
>    *m_pLastString = str;
>    ::PostMessage(AfxGetApp()->m_pMainWnd->m_hWnd, WM_RECEIVE, 0,0);
>    
>    m_pCriticalSection->Unlock();
とありますが、m_pLastStringはどこで使われていますか?
推測ですが、PostMessageで送ったメッセージを処理するどこかで使われると思いますが、PostMessageはSendMessageと違い、メッセージを投げるだけです。そのため、処理がなされているときにデータを受信すると「*m_pLastString = str;」が実行される恐れがあるかと思いますが、これは問題ありませんか?


MSB  2005-02-23 01:42:36  No: 56489

レスありがとうございます
m_pLastStringはメインのダイアログに表示するための文字列を格納しておくために用意しました.お察しの通り,ここのPostMessageで送られたメッセージをダイアログが拾うと,m_pLastStringに格納された文字が表示されるというものです.
そのため恐らくここには特に問題はないのではないかと思います.

ここからは自己レスになりますが,SubSockの基本クラスをCSocketからCAsyncSocketに切り替え,データの入出力をアーカイブを使ったものからウィンドウメッセージとSend関数を使ったものに変えて実行したところ,ちゃんと実行できてしまいました.
原因は明らかにできませんでしたが,おそらくそのあたりに不備があったのだと思います.
どうもお騒がせしました.もしまた機会があれば,そのときはよろしくお願いいたします.


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

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






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