- PR -

[C#]非同期サーバから複数クライアントにメッセージ送信方法について

1
投稿者投稿内容
ぐすん
会議室デビュー日: 2006/04/25
投稿数: 9
投稿日時: 2006-04-25 16:20
はじめて投稿させていただきます。
C#をはじめてまだ間もないのですが、宜しくお願いします。

同一PC内で非同期サーバ、クライアントでデータを送受信する
アプリを作成しています。

非同期のサーバ自体はMSDNのサンプルを参考に作成したのです
が、下記の事柄ができません。
どのように、コーディングすればよいでしょうか?

[質問]
サーバから接続中の複数のクライアント全てに同じメッセージ
を送信したい。

[環境]
WindowsXP
VisualStudio 2005
C#

[コード]
#region 接続要求を受け付け、受信用コールバックを起動
private void Listing()
{
IPAddress ipAddress = IPAddress.Parse(ip);
Sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

try
{
Sock.Bind(new IPEndPoint(ipAddress, Int32.Parse(port)));
Sock.Listen(10);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
return;
}

// サブスレッドを作成する
AcceptThread = new Thread(new ThreadStart(Accept));
AcceptThread.IsBackground = true;
AcceptThread.Start();
}

// 接続要求を受け付けるメソッド
private void Accept()
{
while (true)
{
AcceptDone.Reset();
try
{
Sock.BeginAccept(new AsyncCallback(OnConnectRequest), Sock);
}
catch (ObjectDisposedException ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
}
AcceptDone.WaitOne();
}
}

// 接続要求のコールバック、受信用コールバック起動
private void OnConnectRequest(IAsyncResult ar)
{
try
{
AcceptDone.Set();

Socket listener = (Socket)ar.AsyncState;
handler = listener.EndAccept(ar);

stateObject state = new stateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, stateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
catch (ObjectDisposedException ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
}

}

// 受信用コールバック
private void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
stateObject state = (stateObject)ar.AsyncState;
Socket handler = state.workSocket;

try
{
int ReadSize = handler.EndReceive(ar);

if (ReadSize < 1)
{
MessageBox.Show("クライアント側が切断されました");
return;
}
byte[] bb = new byte[ReadSize];
Array.Copy(state.buffer, bb, ReadSize);
string msg = Encoding.UTF8.GetString(bb);
this.SetText(msg);

handler.BeginReceive(state.buffer, 0, stateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (SocketException ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
return;
}
}
#endregion

#region 送信
private void btn_Send_Click(object sender, EventArgs e)
{
if (handler == null)
{
MessageBox.Show("通信が確立されてません");
}
else
{
SendMsg();
}
}

private void SendMsg()
{
byte[] bb = System.Text.Encoding.UTF8.GetBytes(tbox_Send.Text + "\r\n");

sendDone.Reset();
try
{
handler.BeginSend(bb,
0,
bb.Length,
0,
new AsyncCallback(SendCallback),
handler
);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace);
}
sendDone.WaitOne();
}

//BeginSendに呼び出されるメソッド(EndSend)
private void SendCallback(IAsyncResult ar)
{
Socket client = (Socket)ar.AsyncState;
try
{
int SentSize = client.EndSend(ar);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + "\r\n" + ee.Source + "\r\n" + ee.StackTrace + "\r\n");
return;
}
finally
{
sendDone.Set();
}
}

#endregion
Makoto
大ベテラン
会議室デビュー日: 2004/03/31
投稿数: 133
投稿日時: 2006-04-25 17:53
いつもお世話になっております、

クラス全体のソースがないようですので、予想でコメントします。
間違っていたらゴメンナサイ。

まず、送信する際にbtn_Send_Click内で、(おそらくソケット変数である)
『handler』がいきなり出てきますが、connectされた際に
メンバとして確保している(確保できている)ということと思います。
(その想定で話を進めます。)

※提示されているソースでは、変数がローカル変数なのかメンバ変数
 なのかよくわかりません。

今回は、『複数のクライアントと接続する』ということのようですので、
まずaccept時に、クライアント側へ送信するソケット『handler』を
配列で管理する必要があると思います。

その上で、配列で管理している『クライアントへのソケット:handler』
へ送信関数(beginsend)をループして呼び出せばできそうに思いますが...

なお本コメントは、提示されたソースで、
『サーバ−クライアント1台同士の通信』は行なえているのを前提にしています。
もし、それすらできていないのならば、まずは1台同士の通信を確認したほうが良いと思います。

以上、お忙しいとは思いますが、よろしくお願いいたします。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-04-25 21:37
 サーバが持っている受信処理スレッドを、配列なりコレクションなりで管理して、そいつに「このメッセージを送れ」という指令を出せば(メソッドをコールすれば)いいですよね?

 サンプルを理解することなく使用していることと、
コード:
    // サブスレッドを作成する 
    AcceptThread = new Thread(new ThreadStart(Accept));
    AcceptThread.IsBackground = true;
    AcceptThread.Start();
}


ここで作ったオブジェクトを管理することなく捨てていることが原因です。


 こういうとき、貼り付けたソースが長ければ長いほど、読んでもらえないですよ。
ぐすん
会議室デビュー日: 2006/04/25
投稿数: 9
投稿日時: 2006-04-26 09:52
Makotoさん、Jitterさん返信ありがとうございます。

接続時のクライアントのソケットを配列で持っておいて、
送信する際にループでまわせば問題なく複数のクライアン
トに送信できました。

ありがとうございます。
1

スキルアップ/キャリアアップ(JOB@IT)