Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Общие вопросы > проблема с пониманием логики select


Автор: e7x 11.6.2007, 22:10
ребята, помогите, весь мозг сломал! хоть дубиной меня бейте, никак не могу понять логику работы select для неблокирущих сокетов... 
код я предельно упростил, для концентрации на сути проблемы

$wvec, $rvec, $evec - векторы с установленными битами для обработки действий на соотв. сокетах
$url - глобальный адрес, который нам нужен через прокси
$host - хост

программа иногда вываливается без сообщений об ошибке и видимых причин во время попытки записать что-нибудь в готовый для записи сокет syswrite'ом.

таким образом, код получается рабочий, но непонятным образом глючный...

вот код, место вылета отмечено в комментариях:
Код

        if(socket($sock, AF_INET, SOCK_STREAM, getprotobyname('tcp'))) {
            my $ip = inet_aton($proxy);
            my $paddr = sockaddr_in($port, $ip);
            setsockopt($sock, SOL_SOCKET, SO_SNDTIMEO, $config->{timeout});
            setsockopt($sock, SOL_SOCKET, SO_RCVTIMEO, $config->{timeout});
            fcntl($sock, F_SETFL, O_NONBLOCK);
            syswrite STDOUT, "connecting to $proxy:$port\n";
            connect($sock, $paddr);
        
            vec($rvec, fileno($sock), 1) = 0;
            vec($wvec, fileno($sock), 1) = 1;
            $evec = $rvec | $wvec;
            
            while (select($rovec=$rvec, $wovec=$wvec, $eovec=$evec, $config->{timeout})) {
                if (vec($wovec, fileno($sock), 1)) {
                    # готов для записи
                    syswrite STDOUT, "posting...\n";
                    # !!!!
                    # !!!! ВОТ ТУТ ВЫЛЕТАЕТ
                    # !!!!
                    syswrite $sock, "POST $url HTTP/1.0\nHost: $host\nContent-Type: application/x-www-form-urlencoded\nContent-Length: $cl\nConnection: close\n\n$postcontent";
                    syswrite STDOUT, "posted\n";
                    
                    vec($rvec, fileno($sock), 1) = 1;
                    vec($wvec, fileno($sock), 1) = 0;
                    $evec = $rvec | $wvec;
                } elsif (vec($rovec, fileno($sock), 1)) {
                    # готов для чтения
                    syswrite STDOUT, "reading reply...\n";
                    my $buf;
                    while (my $bytesread = sysread $sock, $buf, 512) {
                        print $buf;
                    }
                    syswrite STDOUT, "read\n";
                    
                    vec($rvec, fileno($sock), 1) = 0;
                    vec($wvec, fileno($sock), 1) = 0;
                    $evec = $rvec | $wvec;
                } else {
                    # ошибочка
                    syswrite STDOUT, "socket error\n";
                }
            }
            syswrite STDOUT, "select timeout or error\n";
        } else {
            syswrite STDOUT, "cannot create socket\n";
        }


сдается мне, я неверно понял логику работы select, и, собственно, надеюсь на вашу помощь
повторюсь, сокеты неблокирующие (Blocking => 0)

===========

проблему решил сам, и, как всегда, все просто
в очередной раз спасибо Стивенсу и его "разработке сетевых приложений", и я еще раз убедился в том, что интернет - помойка. надеюсь, кто-то потом будет искать и найдет этот топик.

Итак, решение проблемы:
нужно было перед каждым ридом/врайтом сокета проверять getsockopt($sock, SOL_SOCKET, SO_ERROR)

кому интересно, вот финальный код:
Код

            while (select($rovec=$rvec, $wovec=$wvec, $eovec=$evec, $config->{timeout})) {
                if (vec($wovec, fileno($sock), 1)) {
                    # готов для записи
                    my $serr = getsockopt($sock, SOL_SOCKET, SO_ERROR);
                    if ($serr ne "\0\0\0\0") { 
                        .... - с сокетом что-то случилось, писать нельзя
                    } else {
                        ... - тут спокойно пишем
                    }                    
                    vec($rvec, fileno($sock), 1) = 1;
                    vec($wvec, fileno($sock), 1) = 0;
                    $evec = $rvec | $wvec;
                } elsif (vec($rovec, fileno($sock), 1)) {
                    # готов для чтения
                    my $serr = getsockopt($sock, SOL_SOCKET, SO_ERROR);
                    if ($serr ne "\0\0\0\0") { 
                        .... - с сокетом что-то случилось, читать нельзя
                    } else {
                        ... - тут спокойно читаем
                    }                    
                    
                    vec($rvec, fileno($sock), 1) = 0;
                    vec($wvec, fileno($sock), 1) = 0;
                    $evec = $rvec | $wvec;
                }
            }


всем спасибо и доброе утро =)
плодотворная ночка выдалась

Автор: nitr 11.6.2007, 22:17
Посоветую вам использовать и прочесть IO::Select
perldoc IO::Select , существенно облегчит вам жизнь.

Добавлено через 2 минуты и 16 секунд
и у вас некое запутанное понимаение select, даже по доке иначе:
Цитата

select
Returns the currently selected filehandle. Sets the current default filehandle for output, if FILEHANDLE is supplied. This has two effects: first, a write or a print without a filehandle will default to this FILEHANDLE. Second, references to variables related to output will refer to this output channel. For example, if you have to set the top of form format for more than one output channel, you might do the following: 
    select(REPORT1);
    $^ = 'report1_top';
    select(REPORT2);
    $^ = 'report2_top';

FILEHANDLE may be an expression whose value gives the name of the actual filehandle. Thus:
    $oldfh = select(STDERR); $| = 1; select($oldfh);

Some programmers may prefer to think of filehandles as objects with methods, preferring to write the last example as:
    use IO::Handle;
    STDERR->autoflush(1);

Автор: e7x 11.6.2007, 22:22
nitr, селекта в перле две штуки ;) 
первая - описанная вами
вторая - та, что использую я 

спасибо, сейчас IO::Select подергаю

Добавлено через 10 минут и 59 секунд
ой, IO::Select не получится использовать. метод can_read возвращает массив хендлов, а метод select 3 ссылки на массив хендлов. мне же не просто хендлы нужны, мне еще нужно знать какой адрес успешно присоединился smile 

Автор: e7x 12.6.2007, 04:28
проблема решена, см. первый пост

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)