Сокетное программирование в perl, проблема со слоем perlio? - PullRequest
1 голос
/ 09 июня 2010

Я заметил некоторые проблемы со слоем perlio в perl. У меня был хороший день, чтобы отследить это и надеяться, что другие люди что-то знали об этом? Самое страшное в этом то, что, поскольку он настолько низкоуровневый, я боюсь, что это снизит переносимость кода.

Код сервера:

use strict;
use Socket;

socket(my $sock, AF_INET, SOCK_STREAM, getprotobyname('tcp')) or die();
setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1) or die();
bind($sock, pack_sockaddr_in(23457, inet_aton('0.0.0.0'))) or die();
listen($sock, 10) or die();

my $paddr = accept(my $csock, $sock);
if (not $paddr) { 
    die();
}
my ($port, $iaddr) = unpack_sockaddr_in($paddr);
printf "accepted %s:%s\n", inet_ntoa($iaddr), $port;
send($csock, "1234567890", 0);
recv($csock, my $tmp, 8192, 0);
close($csock);
close($sock);

Код клиента (который я немного изменил, чтобы проверить):

use strict;
use Socket;
use PerlIO;

socket(my $sock, AF_INET, SOCK_STREAM, getprotobyname('tcp')) or die();
connect($sock, pack_sockaddr_in(23457, inet_aton('localhost'))) or die();
print "layers before = ".join(', ', PerlIO::get_layers($sock))."\n";
#binmode($sock, ':pop');  # uncomment this line to watch the code work...
print "layers after  = ".join(', ', PerlIO::get_layers($sock))."\n";

my $tmp;
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "8192ret = ".read($sock, $tmp, 8192)."\n"; print "tmp $tmp\n"; stillpending($sock);
send($sock, 'blah', 0);

close($sock);

Выход сервера:

accepted 127.0.0.1:39944

Вывод клиента с закомментированным binmode (используется perlio layer):

layers before = unix, perlio
layers after  = unix, perlio
1ret    = 1
tmp 1
no more
1ret    = 1
tmp 2
no more
1ret    = 1
tmp 3
no more
1ret    = 1
tmp 4
no more

Над блоками навсегда.

Вывод клиента без комментария в бинмоде (без использования perlio-слоя):

layers before = unix, perlio
layers after  = unix
1ret    = 1
tmp 1
still more
1ret    = 1
tmp 2
still more
1ret    = 1
tmp 3
still more
1ret    = 1
tmp 4
still more
8192ret = 6
tmp 567890
no more

Моя проблема в том, что select() перестает возвращать эти данные в ожидании, когда очевидно (через strace) первый вызов read () израсходовал весь вывод, отправленный сервером (во некоторый внутренний буфер, который я представляю). После чего последний read(..., 8192) также будет блокироваться, если без слоя perlio он не блокируется.

Полагаю, у меня есть решение моей проблемы (добавьте слой perlio), но мне интересно, что думают другие люди? Является ли ошибкой то, что select() сообщает no more данные, ожидающие обработки, даже при первом чтении perl (со слоем perlio) прочитало все в память?

Кто-нибудь еще сталкивался с подобными проблемами?

1 Ответ

8 голосов
/ 09 июня 2010

Это ожидается: если вы используете select(), вам нужно использовать sysread() вместо read() из-за буферизации (как вы обнаружили:).

From perldoc -f select:

ПРЕДУПРЕЖДЕНИЕ. Не следует пытаться смешивать буферизованный ввод / вывод (например, «чтение» или) с «выбором», за исключением случаев, разрешенных POSIX, и даже тогда только в системах POSIX.Вместо этого вы должны использовать «sysread».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...