[RESOLVED] [2008] MSDN Multi-User TCP Chat Sample Thread Broken
Hi All,
I'm pretty new to VS.NET and trying to get my head around sockets and threading at the min.
I found a tutorial on the MSDN site that I thought was helpful and I understood what was happening but I've downloaded their code and it doesn't work.
I had to change a few lines of code to get it to even connect.
I changed the port number to 5001 (I guess something on my system is already using port 5000).
I also changed a line of code in SocketServer to be:
mobjListener = New TcpListener(IPAddress.Any, 5001)
Whereas before it was just:
mobjListener = New TcpListener(5001)
On something connecting, it's now complaining about the update list box being called from a thread other than the one that created it.
The call to this update is:
Code:
Private Sub OnLineReceived(ByVal sender As Client, ByVal Data As String)
UpdateStatus("Line:" & Data)
Dim objClient As Client
Dim d As DictionaryEntry
For Each d In mcolClients
objClient = d.Value
objClient.Send(Data & vbCrLf)
Next
End Sub
And the procedure it calls is:
Code:
Private Sub DisplayText(ByVal t As String)
txtDisplay.AppendText(t)
End Sub
AIUI, We create a new thread in the variable we declared for this, mobjThread and pass it the address of DoListen so the Sub DoListen will run in that thread. We then issue the start command to the thread to start it running.
We then wait for an incoming connection in this new thread. By running on a new thread, the GUI use is not affected. When a connection is received, we create a new Client and add it to our list of clients.
We ensure we've added the EventHandlers for this new client so when data is received, we call a sub.
The data handler that's causing the issue at the minute (I stepped through the code) is OnLineReceived and we declared this as
Code:
AddHandler x.LineReceived, AddressOf OnLineReceived
So, when data is received by the client x, we call OnLineReceived and run the sub.
Which thread would this be called from? Would it be from the same thread that we created the client in, which was created in the DoListen sub which is running in the mobjThread?
If so, then obviously it can't be allowed to update Windows Forms from that thread - but why would MSDN show this as an example?
If I'm right, then this line here:
Code:
UpdateStatus("Line:" & Data)
is what's causing the problem as by calling it from within the OnLineReceived Sub which is running in the mobjThread, it will be also running in the mobjThread.
I guess I now need to somehow make a DisplayInvoker to marshall that across threads to get it onto the screen - but here I'm stuck.
I think I've learnt quite a bit about threads and the way VB.NET works so far but not enough to know what to do next.
Any help would be greatly appreciated - and apologies for the long post!
I've included all of the code below in case anyone wants to see any other bits.
Thanks Again,
Adam
Code:
mports System.Threading
Imports System.Net
Imports System.Net.Sockets
Public Delegate Sub StatusInvoker(ByVal t As String)
Public Class Form1
Inherits System.Windows.Forms.Form
Private mobjThread As Thread
Private mobjListener As TcpListener
Private mcolClients As New Hashtable()
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Friend WithEvents lstStatus As System.Windows.Forms.ListBox
'Required by the Windows Form Designer
Private components As System.ComponentModel.Container
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.lstStatus = New System.Windows.Forms.ListBox
Me.SuspendLayout()
'
'lstStatus
'
Me.lstStatus.Dock = System.Windows.Forms.DockStyle.Fill
Me.lstStatus.Location = New System.Drawing.Point(0, 0)
Me.lstStatus.Name = "lstStatus"
Me.lstStatus.Size = New System.Drawing.Size(292, 264)
Me.lstStatus.TabIndex = 0
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 273)
Me.Controls.Add(Me.lstStatus)
Me.Name = "Form1"
Me.Text = "Socket Server"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
mobjThread = New Thread(AddressOf DoListen)
mobjThread.Start()
UpdateStatus("Listener started")
End Sub
Private Sub DoListen()
Try
mobjListener = New TcpListener(IPAddress.Any, 5001)
mobjListener.Start()
Do
'Dim x As New Client(mobjListener.AcceptSocket)
Dim x As New Client(mobjListener.AcceptTcpClient)
AddHandler x.Connected, AddressOf OnConnected
AddHandler x.Disconnected, AddressOf OnDisconnected
'AddHandler x.CharsReceived, AddressOf OnCharsReceived
AddHandler x.LineReceived, AddressOf OnLineReceived
mcolClients.Add(x.ID, x)
Dim params() As Object = {"New connection"}
Me.Invoke(New StatusInvoker(AddressOf Me.UpdateStatus), params)
Loop Until False
Catch
End Try
End Sub
Private Sub OnConnected(ByVal sender As Client)
UpdateStatus("Connected")
End Sub
Private Sub OnDisconnected(ByVal sender As Client)
UpdateStatus("Disconnected")
mcolClients.Remove(sender.ID)
End Sub
'Private Sub OnCharsReceived(ByVal sender As Client, ByVal Data As String)
' UpdateStatus("Chars:" & Data)
'End Sub
Private Sub OnLineReceived(ByVal sender As Client, ByVal Data As String)
UpdateStatus("Line:" & Data)
Dim objClient As Client
Dim d As DictionaryEntry
For Each d In mcolClients
objClient = d.Value
objClient.Send(Data & vbCrLf)
Next
End Sub
Private Sub UpdateStatus(ByVal t As String)
lstStatus.Items.Add(t)
lstStatus.SetSelected(lstStatus.Items.Count - 1, True)
End Sub
Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
mobjListener.Stop()
End Sub
End Class
Re: [2008] MSDN Multi-User TCP Chat Sample Thread Broken
Welcome to the forums.
Have a look inside your DoListen subroutine, specifically at this line:
VB.NET Code:
Me.Invoke(New StatusInvoker(AddressOf Me.UpdateStatus), params)
This will invoke the UpdateStatus method on the UI thread, which is needed.
But then, in your OnConnected, OnDisconnected and OnLineReceived methods, you are calling UpdateStatus directly. UpdateStatus will therefor be invoked on the worker thread, which gives you the error you're experiencing.
Re: [2008] MSDN Multi-User TCP Chat Sample Thread Broken
Thanks - I figured what was wrong but just not how to fix it! It works great now :)
Thanks again for your help, I'll mark this thread as resolved now.