DOBON.NET DOBON.NETプログラミング掲示板過去ログ

接続切断判別

  • 題名: 接続切断判別
  • 著者: smith
  • 日時: 2004/11/22 5:32:05
  • ID: 7575
  • この記事の返信元:
    • (なし)
  • この記事への返信:
  • ツリーを表示
環境/言語:[VB.NET 2002]
分類:[.NET]

簡単なチャットソフトのようなものを作りたい(というかほとんどできている)のですが、
サーバ側が一方的に接続をCloseしてもクライアント側が切断されたことに気づかなくて困っています。
もちろん、サーバ側のソフトをEndしてしまえば、
クライアントがメッセージを送信しようとしたときに失敗してエラーが出るのですが、
サーバ側でTcpListenerがStartしてしまっているためか、
接続をCloseしただけだとクライアントはその後もメッセージの送信に成功してしまい、
エラーが出ません。(エラーが出てほしい)
そのチャットソフトの構造は以下のとおりです。


System.IO、System.Text、System.Net.SocketsをImportsしておきます。
sessionをTcpClient型で宣言しておき、
サーバ側では、

Dim TcpListener As TcpListener = New TcpListener(1000)
TcpListener.Start()
While TcpListener.Pending = False
Application.DoEvents()
End While
session = TcpListener.AcceptTcpClient()

として接続要求待ちと要求受信後の接続を行い、
クライアント側では、

Do
Try
session = New TcpClient("192.168.0.2", 1000)
Exit Do
Catch
Application.DoEvents()
End Try
Loop

として接続に成功するまで接続を試み続けます。
接続後、サーバ、クライアントとも、お互いにメッセージの送受信方法は同じで、
メッセージの受信は、

Dim ns As NetworkStream = session.GetStream()
Dim br As New BinaryReader(ns)
Dim mlength(0) As Byte
Dim data As Byte()
While ns.DataAvailable = False
Application.DoEvents()
End While

で受信待ち、

mlength = br.ReadBytes(1)
data = br.ReadBytes(mlength(0))
TextBox2.AppendText(Encoding.Unicode.GetString(data) & vbCrLf)

で受信と受信したメッセージをTextBox2に追記していきます。
メッセージの送信は、

Dim ns As NetworkStream = session.GetStream()
Dim bw As New BinaryWriter(ns)
Dim mbytes As Byte() = Encoding.Unicode.GetBytes(TextBox1.Text)
Dim mlength(0) As Byte = mbytes.Length
bw.Write(mlength)
bw.Write(mbytes)

でTextBox1内の文字列を送信します。
ここまでは問題ないのですが、サーバ側が一方的に接続を切りたい場合、
サーバ側でsession.Close()としても、クライアントはメッセージの送信時にエラーを出さず、
サーバをEndして終了してしまうと、
クライアントがメッセージを送信しようとしたときに失敗してエラーが出ます。
切断する際にクライアントに「切断するぞ」というような信号を送信すればよいのですが、
インターネットを経由していたりして接続が不安定になっているときなど、
「サーバがメッセージを送信しようとしたときに失敗したらそのクライアントはClose」
というようにしたいので、
その後クライアントがメッセージを送信しようとしたときに接続が安定していると、
送信に失敗したことにならず、エラーが出ません。

これは、完全に接続を切断できていないということなのでしょうか。
正しい切断方法や対策法がありましたら教えてください。
説明が長々しくて分かりにくいかもしれませんが、よろしくお願いします。
その手のプログラムを作ったことはないのですが、こんな風にしたらいけるのかなぁ・・・というのを思いついたので参考まで。

実際の接続に加えて、サーバ側で接続フラグを併用して管理する

[通常の切断手順]
1.サーバ側でフラグを「切断」にする
2.実際に接続を切断することをクライアントに通知
3.クライアントは切断の通知を受け取ったらサーバに切断を通知し切断
4.クライアントの切断通知を受け取ったらサーバ側でも切断

[切断失敗時の再切断]
サーバはメッセージを受け取ったときにそのクライアントに対する接続フラグを確認し、
もしフラグが「切断」であれば改めて切断を通知。
クライアント側からも再度通知。
クライアント側の切断通知を受け取るまで内部的には接続状態を保つ。

サーバ側がクライアントの切断通知受け取りに失敗した時のために
一定時間ごとに接続確認するようにしてタイムアウトさせるなどの処理を併用するといいかもしれません。


・・・というのでどうでしょうか?
smithさん、秋さんこんにちは

横槍失礼します。

試してはいないのですが・・・
TcpClient.ClientでSocketクラスが取得できるようです。
SocketクラスのConnectedプロパティで接続状態が拾えるようなので
それを監視してはどうでしょうか?
  • 題名: Re[2]: 接続切断判別
  • 著者: smith
  • 日時: 2004/11/22 20:54:58
  • ID: 7593
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No7580 秋さんへ

わざわざ考えてくださりありがとうございます。
もしかしたらこの手の質問には誰も応えてくれないかもしれないと思っていたので、
とてもうれしいです。


秋さんの考えと似たような感じで、
クライアントからのメッセージ受信時にサーバが応答信号を送信するようにし、
「クライアントが送信したときサーバからの応答が指定時間内に帰ってこなかったら切断と判断」
とすればできそうな気がするのですが、

・切断判断に若干時間がかかる。
・速度が不安定なだけで切断されていないのに誤判断する恐れがある。
・やりとりする情報量が多くなるためサーバに負担がかかる。

などの問題が考えられるので、なるべくならこの方法は使いたくありません。
特にクライアント多数の場合、サーバの処理が間に合わなくなり、
これらの問題発生確率はかなり高くなると思います。
どうしても手段がなかった場合、最終手段として使います。
■No7589 ゆさんへ

> TcpClient.ClientでSocketクラスが取得できるようです。
> SocketクラスのConnectedプロパティで接続状態が拾えるようなので
> それを監視してはどうでしょうか?

情報ありがとうございます。
早速、後で試してみます。
  • 題名: Re[4]: 接続切断判別
  • 著者: smith
  • 日時: 2004/11/24 2:22:29
  • ID: 7610
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
■No7589 ゆさんへ

>TcpClient.ClientでSocketクラスが取得できるようです。
>SocketクラスのConnectedプロパティで接続状態が拾えるようなので
>それを監視してはどうでしょうか?

やってみたのですが、まず、
このプログラムは接続方法がSocketをConnectする方法ではないので、
やり方が分かっていないだけかもしれませんが、Connectedは拾えませんでした。

次に、サーバでNetworkStreamもCloseできることに気づき、やってみたのですが、
すると、クライアントが送信しようとしたときにエラーを出しました。
Session.Close()の際、ns.Close()も行えばクライアントが送信に失敗するようになるようです。

これで何とかなりそうです。ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板