Как я и обещал в прошлой статье про Tcl сокеты сегодня будем разбираться с тем, что такое асинхронные сокеты и как с ними работают. Но для начала, небольшая, но хорошая новость.
Этим летом вышла книга про сетевое программирование на Tcl, что не может остаться без внимания сообщества программистов. На русском книги этой нет и вряд ли будет :_(
Подумываю теперь, где бы сэкономить $50
Далее по теме.
Чем отличаются асинхронные сокеты?
Иногда, соединение с сервером является достаточно длительным процессом. В этом случае команда открытия сокета в обычном синхронном режиме остановит исполнение всего скрипта до того момента, пока не будет выполнено подключение к серверу.
Использование асинхронного сокета позволяет скрипту исполняться, пока происходит подключение к серверу. Что бы открыть сокет в асинхронном режиме нужно использовать опцию ‘-async’.
set channel [socket -async $address $port] |
Блокирующий режим
Опция -async относится только к подключению, но не к обмену данными с каналом. Сразу после создания канала он переводится в блокирующий режим, что эквивалентно синхронному обмену данными с каналом.
Это значит, что скрипт будет приостанавливаться, если:
- вызвана команда gets или read, но в канале нет данных
- вызвана команда flush, но еще нет соединения
- вызвана команда puts, буфер полон, но еще нет соединения
В случае gets и read скрипт будет приостанавливаться до появления данных в канале, или до разрыва соединения (тогда вернут ошибку).
В следующем примере соединение выполнено асинхронно, но из за того, что канал находится в блокирующем режиме, вызов gets, вызванный сразу после socket, приостановит исполнение скрипта.
set channel [socket -async yandex.ru 99] set data [gets $channel] ... тут долго ждем ... |
Правда, этот пример в конце ожидания еще выдаст ошибку
т.к. я специально выбрал несуществующий сервер, что бы подольше подключалось, дабы имитировать сеть с низкой пропускной способностью.
Неблокирующий режим
В неблокирующем режиме вместо задержки выдается ошибка, которую нужно отловить и соответствующим образом обработать. Ошибки возникают в следующих конкретных случаях:
- Вызывается gets или read, когда в канале нет данных
- Вызывается flush, когда нет подключения
- Вызывается puts, когда нет подключения и буфер заполнен
Что бы перевести канал в неблокирующий режим, сразу после подключения нужно использовать команду fconfigure:
set channel [socket -async $address $port] fconfig $channel -blocking false |
В случае если из канала выбраны все данные, то команда fblocked будет возвращать 1.
В случае неудачного подключения
Если подключение завершилось неудачно, то команды чтения из канала и flush будут сразу возвращать ошибку, а команды записи в канал только по его заполнению.
ВАЖНО! Если еще нет соединения и буфер заполнен, команда после которой буфер был заполнен закончится ошибкой, а следующие команды записи будут перезаписывать буфер с начала! По этому важно отслеживать эти ошибки, что бы не допустить потери информации.
Пример использования fblocked
Сервер отсылает порцию данных и не закрывает канал. Такое возможно, когда сервер ожидает ответа от клиента.
proc connectionHandler {channel host port} { puts "Новое соединение с $host" puts $channel { (__) (oo) /-------\/ / | || * ||----|| ~~ ~~ } flush $channel } socket -server connectionHandler 9909 puts "Hi coder :)" puts "server address: localhost 9909" vwait forever |
Клиент работает в неблокирующем режиме. Так как канал не был закрыт, клиент может узнать о конце передачи данных только с помощью команды fblocked. Хотя, в общем случае, это означает только то, что данные во входном буфере были исчерпаны. Например, это может случиться при задержке очередного пакета.
(интерактивный режим tclsh)
% socket localhost 9909 sock224 % fconfig sock224 -blocking false % while {[fblocked sock224] == 0} { puts [gets sock224] } (__) (oo) /-------\/ / | || * ||----|| ~~ ~~ % _ |
Конец
Следующий раз опишу полноценную реализацию небольшого протокола, которая будет полностью иллюстрировать описанные выше тонкости работы с сокетами.






Последние комментарии