A TCP Socket in a Thread

It’s late and the OOP side of my brain is failing me. I create a custom thread class and a custom tcp socket class in the IDE. A method in the window open event instantiates the thread in code assigning it to a property and a method in the thread instantiates the socket in code assigning that to a property. How do I get code in an event the in the socket to address methods and properties in the thread once they’ve been instantiated? Brain freeze, help !!

Have the thread pass itself to the socket, ideally in the constructor.

theSocket = new MyTCPSocketClass(me)

However, I don’t think you gain much having the thread instantiate a socket, since they’re pretty much independent anyway - the socket’s events all occur on the main thread, not in your thread. It might be better to have the window instantiate both the thread and the socket and act as the go-between.

[quote=63324:@Tim Hare]Have the thread pass itself to the socket, ideally in the constructor.

theSocket = new MyTCPSocketClass(me)

However, I don’t think you gain much having the thread instantiate a socket, since they’re pretty much independent anyway - the socket’s events all occur on the main thread, not in your thread. It might be better to have the window instantiate both the thread and the socket and act as the go-between.[/quote]

This is not correct. They do not necessarily happen on the main thread. I use threads and sockets extensively and socket events absolutely happen in threads for the most part - particularly writes and so forth. Asynchronous events like DataAvailable may happen on the main thread but they may not. I’ve seen both for the same piece of code. It all depends on how the event gets fired. If you call socket.poll from your thread, then the DataAvailable event fires in the thread. If you let it fire as the data arrives, it happens on the main thread.

So create methods for all the events that you want to handle for the socket. Set up AddHandler directives to use those methods to handle the events. Set up the AddHandler as the thread starts if the socket is local to the thread and will be destroyed once the thread is gone. Otherwise, set up the AddHandler in the main thread so you don’t call it each time the thread runs and you’ll get errors.

Jon could you post a code example from your two posts? Thank you very much.

Well, for example…

I have an object in code that represents a real world device. I connect to that device via a TCP socket that is a property of that object. So in my constructor method I have;

  TCPSock = New TCPSocketPlus
  AddHandler TCPSock.DataAvailable, AddressOf TCPDataAvailable
  AddHandler TCPSock.Error, AddressOf TCPSockErrorEvent
  
  AuxSocket = New TCPSocketPlus
  AddHandler AuxSocket.Error, AddressOf TCPSockErrorEvent

TCPSocketPlus is a custom subclass of TCPSocket.

Now, for my Methods used:

Sub TCPDataAvailable(t as TCPSocket)
  If t.Lookahead(Encodings.UTF8).InStr("login") <> 0 Then
    
    If Not DidLogin Then
      
      Dim junque as String = t.ReadAll
      
      Write("root"+EndOfLine)
      DidLogin = True
      Connected = True
      'System.Log(System.LogLevelNotice,"Did the device login")
    End If
    
  End If
  
  If LoggedIn = False And t.Lookahead(Encodings.UTF8).InStr("BusyBox") <> 0 Then
    LoggedIn = True
  End If
  
  If WorkToDo = WorkTasks.ParseData Then
    
    If Not Queued Then
      ThreadPoolManager.Queue(me)
      Queued = True
    End If
       
 End If
  
End Sub

And

Sub TCPSockErrorEvent(t as TCPSocketPlus)
  If Not t.SockConnected or t.LastErrorCode <> 0  Then
    Connected = False
    TCPSock.SockConnected = False
    AuxSocket.SockConnected = False
    TCPSock.Close
    AuxSocket.Close
    
    RaiseEvent BoxDisconnected
    
    If ReconnectTimer.Mode = Timer.ModeOff Then
      ReConnectTimer.Mode = Timer.ModeSingle
      'ReConnectTimer.Reset
    End If
    
  End If
End Sub

Now, as far as calling a socket from a thread, I don’t think I necessarily need to show you how to do that. But there I’ve opened my kimono and showed you how to use methods as event handlers.

Now for threading, I have an class interface defined for my object where I send the entire object to my ThreadPoolManager. The ThreadPoolManager calls a method of the class interface called “DoWork.” In DoWork, I decide what I want to do in the thread based on the Enum value of WorkToDo. In my DoWork method I have a Select Case Statement that branches based on the Enum. You can see up there in the code where I set a value. So for example, WorkTasks.ParseData is what I use to parse the initial data and parameters of the device I connect to upon login. All that is done in a thread and all of it is written in a thread. I limit my ThreadPoolManager to 15 active threads. Any additional objects sent to the manager are queued and wait until a thread becomes available. I have Aaron Ballman I believe to thank for that piece of code (it’s either him or Alex Restreppo - I forget which).

Now, in another piece of code, I have the following for the run event of a thread in my app. This thread doesn’t use the ThreadPoolManager. All this is done in the thread with the socket write’s happening in a thread. I know they are threaded because all the writes happen in the thread.

Sub GrabThreadRun(th as thread)
  #pragma Unused th
  
  Do
    SourceDevice.AuxSocket.Write("SOME_COMMAND"+EndOfLine)
    SourceDevice.AuxSocket.Write("SOME_OTHER_COMMAND"+EndofLine)
    app.SleepCurrentThread(SleepTime)
  Loop
  
End Sub

In the DataAvailable event for the socket I have this:

Sub GrabSocketDataAvailable(t as TCPSocket)

  If Draw Then
     Dim s as String
    Dim p as Picture
    
    s = t.Lookahead
    p = BMPStringtoPictureMBS(s)
    
    If p <> Nil Then
      PicArray.Append p
      me.Refresh
      s = t.Readall
      t.listen     
    End If
  End If
  
End Sub

Now, this runs on the main thread. The “Me” object here is a canvas. I can refresh the canvas and it works just fine on the main thread because I let the DataAvailable event fire on the main thread. If I were to call

SourceDevice.AuxSocket.Poll

from the Thread above, the DataAvailable event would be called from within the thread and I would get a “ThreadAccessUIException” thrown. Trust me. I’ve done it and confirmed it multiple times.

Did this help?

Crap. Wrong Tags put into the editor. Needed “[” instead of “<” and can’t edit posts which is a PITA.

Yes Joe thank you very much!!

I haven’t had a chance to read the rest of a thread, but here’s how sockets work:

  • The main thread’s event loop services each socket and fires events as needed. So, without having to call poll, sockets will work and their events will fire on the main thread.
  • User code calls Poll on a socket. This can fire events for that socket on whatever thread Poll got invoked on.

There usually shouldn’t be a need to call Poll, as long as your main event loop isn’t blocked (which it shouldn’t be because that just provides a bad user experience).

If you’re Polling from the thread, isn’t there some chance that the main thread will also poll the socket? You really can’t be sure which thread the event will eventually fire in, so don’t count on it. Unless you block the main thread while you’re polling via a pragma, but doesn’t that defeat the purpose of the thread?

No, not at all. It is possible that the main thread will poll the socket, but likely if your thread is running, it will invoke it first if you so choose to do it that way. However, having code in DataAvailable is not necessary. You can always in a thread use lookahead to see if there is data in the buffer. Then you can process that data in the thread. So there’s a lot of ways to skin the cat here.

[quote=63410:@Joe Ranieri]
There usually shouldn’t be a need to call Poll, as long as your main event loop isn’t blocked (which it shouldn’t be because that just provides a bad user experience).[/quote]

Actually, it’s many times faster to call poll in order to force the DataAvailable event to fire. Additionally, if you are doing a connect loop like:

Socket.Connect
Do Until Socket.IsConnected
Socket.Poll
Loop

That allows you to run a nice loop to be sure the socket connects before moving on.

I would like to do tcp socket in a thread not for main thread. main window holds all is better .

[quote=63601:@Jon Ogden]Socket.Connect
Do Until Socket.IsConnected
Socket.Poll
Loop[/quote]

That’s a tight loop that will block the event loop so poll is required. Too bad about any UI (or any other thread) activity.

I use the connected event to trigger processing, and a timer (which is cancelled by the connected event handler) to trigger a retry or initiate a connection failed process.