Почему в этом примере AnyEvent::Handle не вызывается on_eof?

Это мой простой сервер. Когда я запускаю его и подключаюсь к нему через telnet (порт 5222), и когда telnet завершает соединение, почему не вызывается моя функция on_eof? Т.е. почему не строка "КАТАСТРОФА!!!" печататься?

#!/usr/bin/perl

use v5.18;
use warnings;

use EV;
use AnyEvent;
use AnyEvent::Socket;
use AnyEvent::Handle;

our $hdl;

my $server = tcp_server undef, 5222, sub {
    my ($fh) = @_;

    $hdl = AnyEvent::Handle->new(fh => $fh);
    $hdl->on_eof(sub {
        my ($handle) = @_;

        say "CATASTROPHE!!!";
    });
};

EV::run;

1 ответ

Решение

tl;dr: без попытки чтения из сокета, EOF не может быть обнаружен. Используйте ->on_eof или ->push_read.

Длинная версия:

Пример не пытается что-либо прочитать из дескриптора, и именно поэтому AnyEvent::Handle не пытается читать данные. Без попытки чтения данных он не может обнаружить EOF (следствие API POSIX).

Это поведение описано только косвенно в описании методов start_read/stop_read:

Обратите внимание, что AnyEvent::Handle автоматически "start_read" для вас при изменении обратного вызова "on_read" или push/unshift чтения обратного вызова, и он будет автоматически "stop_read" для вас, когда не установлено ни "on_read", ни чтения запросы в очереди.

Причина, по которой он ведет себя так, заключается в том, что в программе, основанной на событиях, может быть любое количество времени между чтением данных (выполненных внутри) и постановкой в ​​очередь обратного вызова чтения. Поскольку получение данных, которые не являются ожидаемыми, является ошибкой (и если не сделать так, чтобы ошибка могла переполнить буфер чтения, вызывая ошибку), должен быть какой-то способ избежать этих ошибок.

Не чтение данных, когда ничего не запрашивается, - это автоматический способ избежать ложных ошибок, вызванных тем, что программа не "достаточно быстра" для обработки данных: AnyEvent::Handle просто не читает ничего из сокета, пока программа не примет решение о что делать с данными.

В вашем довольно нетипичном примере вы ничего не делаете с сокетом. Чтобы обнаружить on_eof, вы можете вызвать ->start_read, но это вряд ли будет полезно в реальных программах.

Другие вопросы по тегам