More On the Socket's Asynchronous Nature

Top  Previous  Next

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

Executing sock.connect, sock.close, sock.reset, or sock.discard method 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 topic explained how to find out actual connection status at any time (see 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. Supposing, 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:

 

 

'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:

 

 

'!!! 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 connection will definitely be established.

 

 

'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 even more interesting example. Supposing, you want to close and reestablish a TCP connection right within the same event handler. Here is a wrong way of doing this:

 

 

'!!! 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!

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.

 

 

'!!! '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 the Closing Connections topic ("Socket re-use after connection closing" section) we have already explained that the OS makes sure that the 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 — the on_sock_event won't squeeze in between them unless you use the doevents statement! Here is the correct code:

 

 

'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.

 

 

'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

...

 

note_warning-wt

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!