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