Solution
Ok tonll, I played around with your Server code and got it working - but I changed some stuff a lot and have a few comments:
1) We fix the exception you were receiving by calling
SetApartmentState() on the new thread when we create it.
2) When a connection comes in you are gonna pop up a save box on the server - do you really want to do that everytime?!!?
3) You dont need all those threads you were creating. Really, you just have 2 core threads. One thread listens for incoming connections and one handles the main form window. All other threads are spawned to deal with incoming socket connections.
For that reason I renamed the
Delegate you had at the top, and used the
Me.Invoke mechanism to call cross-thread. If you look at the
SetText method you can see it invokes our delegate. This points at our method
AddToConnections, which executes on the
Form1 thread.
This means you can call
SetText from any thread in your code and it will correctly update the main form.
4) When the Server application ends, you arent closing your listening socket. If you look how I wrapped your
Do..While in a
Try..Catch, we have a mechanism there to handle SocketExceptions - and then in FormClosing we can force the connection to Close(). As I commented, you should also close any open sockets at that time as well.
I think thats everything, but post again if missed something or really dont make sense.
mafro
Imports System.IO
Imports System.Threading
Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Imports System.ComponentModel
Imports System.Windows.Forms
Public Class Form1
Private alSockets As ArrayList
Dim tcpListener As TcpListener
Public textData As String
Private showSave As Thread
Public savePath As String
Private info As String
'declare our delegate
Public Delegate Sub SetTextDelegate(ByVal text As String)
'create an instance of the delegate pointing at our update method
Public SetTextInstance As New SetTextDelegate(AddressOf AddToConnections)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
textData = ""
savePath = ""
Dim IPHost As IPHostEntry
IPHost = Dns.GetHostEntry(Dns.GetHostName())
lblStatus.Text = "IP address: " + IPHost.AddressList(0).ToString()
alSockets = New ArrayList()
'start listener thread
Dim thdListener As New Thread(New ThreadStart(AddressOf listenerThread))
thdListener.Start()
lbConnections.Items.Add("Waiting for connection..")
End Sub
Private Sub Form1_FormClosing(ByVal sender As System.Object, ByVal e As FormClosingEventArgs) Handles MyBase.FormClosing
'interrupt the socket listener here!
tcpListener.Stop()
End Sub
Public Sub listenerThread()
tcpListener = New TcpListener(IPAddress.Any, 8080)
Dim handlerSocket As Socket
Dim thdHandler As Thread
tcpListener.Start()
Try
Do
handlerSocket = tcpListener.AcceptSocket()
If handlerSocket.Connected Then
'storing handlersocket info into string for easy access
info = handlerSocket.RemoteEndPoint.ToString()
'calling another thread to update listbox
Me.SetText("Received connection")
'there is no need to SyncLock here as we only have
'one thread accessing alSockets
alSockets.Add(handlerSocket)
'spawn another thread to handle the connection
thdHandler = New Thread(AddressOf handlerThread)
'set the apartment state of the new thread!!!
thdHandler.SetApartmentState(ApartmentState.STA)
thdHandler.Start()
End If
Loop
Catch ex As SocketException
'do something
Finally
'on error close all sockets and listeners
tcpListener.Stop()
'you need to loop through alSockets and close all connections
End Try
End Sub
Public Sub handlerThread()
Dim handlerSocket As Socket
handlerSocket = alSockets(alSockets.Count - 1)
Dim networkStream As NetworkStream = New NetworkStream(handlerSocket)
Dim blocksize As Int16 = 1024
Dim thisRead As Int16
Dim databyte(blocksize) As Byte
Dim sw As StreamWriter
Dim saveFileDialog1 As New SaveFileDialog()
saveFileDialog1.Filter = "textfile|*.txt"
saveFileDialog1.Title = "Save File"
saveFileDialog1.ShowDialog()
savePath = saveFileDialog1.FileName
SyncLock Me
'creating a streamwriter to write data read from thisRead into file.
sw = New StreamWriter(savePath)
'reading data
thisRead = networkStream.Read(databyte, 0, blocksize)
'storing data read into a string
textData = Encoding.ASCII.GetString(databyte)
'write data into file
sw.Write(textData)
'close streamwriter
sw.Close()
End SyncLock
'call cross-thread to update listbox
Me.SetText("File Written.")
handlerSocket.Close()
End Sub
''' <summary>Call this method to update listbox cross-thread</summary>
''' <param name="text">Text to update</param>
Private Sub SetText(ByVal text As String)
Me.Invoke(SetTextInstance, New Object() {text})
End Sub
Private Sub AddToConnections(ByVal text As String)
'when this method executes it will be on the Form1 thread
'because we use Me.Invoke and the delegate to call cross-thread
Me.lbConnections.Items.Add(text)
End Sub
End Class