Подробнее об асинхронной природе сокета
В разделах Установление исходящих соединений и Закрытие соединений мы уже касались асинхронной природы объекта sock. Здесь описано подробнее, что это значит для вашего приложения.
Выполнение методов sock.connect, sock.close, sock.reset или sock.discard не означает, что к моменту перехода программы к следующему оператору соединение уже установлено или разорвано. Эти операторы лишь сообщают Master Process, что делать с соединением. Установление и разрыв соединения могут занять некоторое время, и приложению не обязательно ждать их завершения. В разделе Проверка состояния соединения объяснялось, как в любой момент узнать фактическое состояние соединения (см. свойства только чтение sock.state и sock.statesimple).
Бывают ситуации, когда программе приходится это учитывать. Вот один пример. Допустим, нужно узнать MAC-адрес удалённого устройства, к которому мы устанавливаем исходящее соединение. Это можно сделать так:
'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
Это хороший пример событийно-ориентированного программирования. Но иногда соединение нужно установить и «довести до конца» в том же обработчике события. Как это сделать? Вот простой, но НЕПРАВИЛЬНЫЙ пример кода:
'!!! 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!
...
А вот правильный способ. Для наглядности в этом примере предполагается, что соединение будет установлено наверняка.
'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!
...
Вот ещё более интересный пример. Допустим, TCP-соединение нужно закрыть и установить заново прямо в одном обработчике события. Неправильный способ:
'!!! BAD EXAMPLE !!! -- this just won't work!
...
...
sock.close
sock.connect
...
Дело в том, что выполнение sock.close на самом деле не закрывает соединение - оно лишь передаёт Master Process инструкцию закрыть его. Поэтому к моменту, когда выполнение программы доходит до метода sock.connect, прежнее соединение всё ещё активно!
Правильно дождаться фактического закрытия соединения и только потом выполнять sock.connect. Вот ещё один пример - тоже не вполне правильный, но уже ближе к истине.
'!!! '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
...
Так лучше. Осталась одна последняя поправка, и код будет готов. В разделе Закрытие соединений (подраздел «Повторное использование сокета после закрытия соединения») мы объяснили, что TiOS гарантирует: on_sock_event успеет выполниться после закрытия старого соединения и до установления нового. В приведённом выше примере и sock.close, и sock.connect находятся в одном обработчике события - on_sock_event не вклинится между ними, если не использовать оператор doevents! Вот правильный код:
'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
...
Обратите внимание, что doevents стоит после цикла while-wend. Это принципиально важно! Конечно, раз в обработчике события теперь есть хотя бы один doevents, можно добавить doevents и во все остальные «места ожидания» - чтобы другие события выполнялись раньше.
'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
...
Оператор doevents нужно ставить после цикла while-wend, причём даже в том случае, если в вашем приложении нет обработчика события on_sock_event!