More On the Socket's Asynchronous Nature

In Establishing Outgoing Connections and Closing Connections, we touched on the subject of the sock. object's asynchronous nature. This topic offers further details on what that means for your application.

Executing the sock.connect, sock.close, sock.reset, or sock.discard methods does not mean that your connection gets established or terminated by the time your program reaches the next statement. Executing these statements merely instructs the Master Process what to do with the connection. Connection establishment/termination can take some time and your application doesn't have to wait for that to complete. Checking Connection Status explained how to find out the actual connection status at any time (see the sock.state and sock.statesimple read-only properties).

There are certain situations when your program has to take the above into account. Here is one example. Suppose we want to know the MAC address of a remote device to which we are establishing an outgoing connection. Naturally, we can do it this way:

Tibbo BASIC
'Correct code -- on startup we order the connection to be established and in the on_sock_event event handler we record the MAC  address of the 'other side'
 
Sub On_sys_init
   ...
   sock.targetip="192.168.100.40"   'we prepare for the connection
   sock.targetport=2000
   sock.connect             'and now we connect
End Sub
 
'------------------------------------------------------------------------------------
Sub On_sock_event(newstate As pl_sock_state,newstatesimple As pl_sock_state_simple)
   Dim s As String
   If newstatesimple=PL_SSTS_EST Then
      'OK, so connection is established, let's get this MAC!
      s=sock.remotemac
   End If
End Sub

The above is a good example of event-driven programming. Sometimes, however, you need to establish a connection and "follow up" on it in the same event handler. So, how do we do this? Here is a simple, and WRONG, code example:

Tibbo BASIC
'!!! BAD EXAMPLE !!!
 
Dim s As String
...
...
 
sock.targetip="192.168.100.40"   'we prepare for the connection
sock.targetport=2000
sock.connect             'and now we connect
s=sock.remotemac         'and now we try to check the MAC. WRONG! Connection may not be established yet!
 
...

And here is the correct way to handle this. For clarity, this example assumes that a connection will definitely be established.

Tibbo BASIC
'Correct, but simplified example (we do not handle possible connection failure).
 
Dim s As String
...
...
 
sock.targetip="192.168.100.40"   'we prepare for the connection
sock.targetport=2000
sock.connect             'and now we connect
While sock.statesimple<>PL_SSTS_EST 'we wait here until the connection is actually established
Wend
s=sock.remotemac         'Get the MAC!
 
...

Here is an even more interesting example. Suppose you want to close and re-establish a TCP connection right within the same event handler. Here is a wrong way of doing this:

Tibbo BASIC
'!!! BAD EXAMPLE !!! -- this just won't work!
...
...
 
sock.close
sock.connect
...

You see, executing sock.close doesn't really close the connection — it only issues the instruction (to the Master Process) to close the connection. So, by the time program execution gets to the sock.connect method, your previous connection is still on!

The correct way is to wait for the connection to actually be closed before executing sock.connect. Here is another example — not quite correct either — but closer to the truth.

Tibbo BASIC
'!!! 'BETTER' CODING !!! -- still not totally OK!
...
...
 
sock.close
   While sock.statesimple<>PL_SSTS_CLOSED 'here we wait for the connection to be closed
   Wend
sock.connect
...

OK, this is better. One final correction and the code is complete. In Closing Connections (in the "Socket Re-Use After Connection Closing" section) we explained that TiOS makes sure that on_sock_event has a chance to execute after the old connection is closed and before the new one is established. In the above example, both sock.close and sock.connect are in the same event handler — on_sock_event won't squeeze in between them unless you use the doevents statement! Here is the correct code:

Tibbo BASIC
'100% CORRECT!
...
...
 
sock.close
   While sock.statesimple<>PL_SSTS_CLOSED 'here we wait for the connection to be closed
   Wend
   doevents ' Absolutely essential for this particular case!
sock.connect
...

Notice how doevents is placed after the while-wend loop. It is absolutely essential that you do it this way! Of course, now that you have at least one doevents in the event handler, you might as well add doevents in all "places of waiting" — just to let other events execute sooner.

Tibbo BASIC
'Even better code!
...
...
 
sock.close
   While sock.statesimple<>PL_SSTS_CLOSED 'here we wait for the connection to be closed
      doevents 'Not necessary but useful -- lets other events execute
   Wend
   doevents ' Absolutely essential for this particular case!
sock.connect
...

A warning note icon.You have to place doevents after the while-wend loop, and you have to do this even if you don't actually have a handler for the on_sock_event event in your application!