質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

解決済

2回答

3660閲覧

VBでサーバクライアントソフトを作成中ですが、サーバからクライアントにデータを送れないです。

HiroyaIto

総合スコア5

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

0クリップ

投稿2020/02/18 20:05

編集2020/02/19 11:41

前提・実現したいこと

VB.netでTCPIPのサーバ、クライアントソフトを作っています。

■■な機能を実装中に以下のエラーメッセージが発生しました。

発生している問題・エラーメッセージ

サーバ側のソフトで質問です。状況として、クライアントからの接続要求で接続後に、
クライアントからの受信はサーバ側のrichtextboxに表示できています。
ところが、サーバからクライアントにデータを送信(writeline)すると、サーバの
受信用richtextboxにデータが表示しようとしてしまって、クライアント側にデータが送られません。

該当のソースコード```

Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Threading

Public Class Form1

Dim ServerStatus As Boolean = False Dim ServerTrying As Boolean = False Dim Server As TcpListener Dim Clients As New List(Of TcpClient) Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load CheckForIllegalCrossThreadCalls = False End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click StartServer() End Sub Function StartServer() If ServerStatus = False Then ServerTrying = True Try Server = New TcpListener(IPAddress.Any, 4305) Server.Start() ServerStatus = True Threading.ThreadPool.QueueUserWorkItem(AddressOf Handler_Client) Catch ex As Exception ServerStatus = False End Try ServerTrying = False End If Return True End Function Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click StopServer() End Sub Function StopServer() If ServerStatus = True Then ServerTrying = True Try For Each Client As TcpClient In Clients Client.Close() Next Server.Stop() ServerStatus = False Catch ex As Exception StopServer() End Try End If Return True End Function Function Handler_Client(ByVal state As Object) Dim TempClient As TcpClient Try Using Client As TcpClient = Server.AcceptTcpClient If ServerTrying = False Then Threading.ThreadPool.QueueUserWorkItem(AddressOf Handler_Client) End If Clients.Add(Client) TempClient = Client 'Dim TX As New StreamWriter(Client.GetStream) Dim RX As New StreamReader(Client.GetStream) If RX.BaseStream.CanRead = True Then While RX.BaseStream.CanRead = True Dim RawData As String = RX.ReadLine Richtextbox1.text += Client.Client.RemoteEndPoint.ToString + ">>" + RawData + vbNewLine End While End If If RX.BaseStream.CanRead = False Then Client.Close() Clients.Remove(Client) End If End Using Catch ex As Exception If TempClient.GetStream.CanRead = False Then TempClient.Close() Clients.Remove(TempClient) End If End Try Return True End Function Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown If e.KeyCode = Keys.Enter Then e.SuppressKeyPress = True If TextBox1.Text.Length > 0 Then SendToClients(TextBox1.Text) TextBox1.Clear() End If End If End Sub Function SendToClients(ByVal Data As String) If ServerStatus = True Then If Clients.Count > 0 Then Try For Each Client As TcpClient In Clients Dim TX1 As New StreamWriter(Client.GetStream) TX1.WriteLine(Data) TX1.Flush() Next Catch ex As Exception SendToClients(Data) End Try End If End If Return True End Function Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click End Sub

End Class

### 試したこと textbox1に入力された文字をクライアントに送る仕様です。 また、クライアントからの文字は、Richtextbox1に表示される仕様です。 サーバからクライアントにデータを送信するときに、デバッガで追いました。 データ送信すると、SendToClientsのTX1.WriteLine(Data)が実行された後、 Handler_Clientが反応してしまって受信用のRichtextbox1が更新されてしまい、 クライアントにデータを送信することができません。 ### 補足情報(FW/ツールのバージョンなど) visual studio 2019 VB ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/02/18 21:55

コードはインデントされるようにしてください。インデントされてないコードは質問者さん自身も読む気がしないのでは? タグは VB.NET の方がよさそうです。付け直すか追加してください。
HiroyaIto

2020/02/18 23:04

仰る通りです。申し訳ありません。本日、再投稿させて頂きます。
y_waiwai

2020/02/18 23:31

このままではコードが読みづらいので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
退会済みユーザー

退会済みユーザー

2020/02/18 23:36 編集

再投稿とのことですが、別に新たにスレッドを立てるのはダメですよ。質問は編集・修正できます。このスレッドの質問欄を編集して質問を続けてください。
YAmaGNZ

2020/02/19 00:59

提示されているコードを動作させてみましたが、仰っている現象が確認できませんでした。 クライアント側の実装も関係あるかもしれません。
guest

回答2

0

ベストアンサー

原因と思われるもの

StreamWriter を GC が回収するとき NetworkStream が Dispose されているようです。

VB

1Using TX1 As New StreamWriter(Client.GetStream, System.Text.Encoding.Default, 1024, True) 2 TX1.WriteLine(Data) 3 TX1.Flush() 4End Using

leaveOpen = True を指定して開いたままにすれば良さそうです。

その他

クライアント側が切断処理を行うと

VB

1While RX.BaseStream.CanRead = True 2 Dim RawData As String = RX.ReadLine 3 Richtextbox1.text += Client.Client.RemoteEndPoint.ToString + ">>" + RawData + vbNewLine 4End While

の部分でループしますね。

クライアントが切断すると RawData は Nothing になるので、そのときループを抜けるようにします。

あと気になった点は

(1) TcpListener を使うなら BeginAcceptTcpClient メソッドを使ったほうが良いかと思います。
https://docs.microsoft.com/ja-jp/dotnet/api/system.net.sockets.tcplistener.beginaccepttcpclient?view=netframework-4.8

(2) Control.CheckForIllegalCrossThreadCalls = False にすると別スレッドからコントロールにアクセスできますが、
シリアライズは自分で責任を持つ必要があります。

VB

1SyncLock RichTextBox1 2 RichTextBox1.Text &= Client.Client.RemoteEndPoint.ToString + ">>" + RawData + vbNewLine 3End SyncLock

のようにしないと複数クライアントから同時にメッセージが送られた時抜ける可能性があります。

(3) サーバーからの切断処理

受信漏れがあっても気にしないのであれば、TcpClient.Close で良いですが、そうでないのであれば
サーバーからの切断は、TcpClient.Client.Shutdown(SocketShutdown.Send) で行います。

通信スレッドの終了を待っていないのもなんとかしたいところです。

(4) クライアントの管理

Dim Clients As New List(Of TcpClient)

マルチスレッドで動作するので排他処理が必要です。

投稿2020/02/19 03:16

編集2020/02/19 08:20
KOZ6.0

総合スコア2622

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

仰っている現象に対しての回答ではありません。

提示されたソースですと、CanReadプロパティで受信データがあるかどうか判断しようとしているように読めます。
CanReadプロパティはそのストリームが読み取りをサポートしているかどうかであって、受信データがあるかどうかではありません。
提示ソースですと、CanReadは常にTrueを返します。
Dim RawData As String = RX.ReadLineの行にブレークポイントを置いてみるといいでしょう。
クライアントが接続された瞬間にその行で止まるはずです。
ReadLineがブロック処理なので1行読み終わるまで結果的にそこで止まっています。

受信データがあるかどうか判断するのであれば、TcpClient.GetStreamが返してくる型どおりNetworkStreamクラスで受けてDataAvailableプロパティで判断すべきです。

例)

VBNET

1Dim stream As NetworkStream = client.GetStream() 2 3If stream.DataAvailable Then 4 Dim readbuffer(256) As Byte 5 stream.Read(readbuffer, 0, 256) 6 7 Dim txt = System.Text.Encoding.ASCII.GetString(readbuffer) 8 RichTextBox1.AppendText(txt & vbNewLine) 9End If

また、受信処理内のTry~CatchにてTempClient.GetStreamへとアクセスしていますが、クライアントから切断された場合、すでにコネクションが閉じられているのでGetStreamメソッドを使用するとSystem.ObjectDisposedExceptionが発生します。
ここは特に条件を設けずにすぐにクライアントを破棄していいかと思います。

SendToClientsにて例外が発生した場合にSendToClientsを呼び出していますが、送信中に切断された場合などに永久的に呼び出されてStackOverflowExceptionが発生しそうです。
エラー時に再送したのでしたら、このあたりも考え直したほうがいいかと思います。

投稿2020/02/19 01:21

編集2020/02/19 01:54
YAmaGNZ

総合スコア10222

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問