Выберите возврат 0 в закрытом сокете SCTP - PullRequest
3 голосов
/ 11 февраля 2010

Это связано с вопросом: SCTP с множественной адресацией в качестве замены для TCP

У меня есть простое эхо-клиент / параллельное серверное приложение, которое отлично работает при использовании TCP. Я мог бы передать файл в stdin на клиенте, и клиент получит все данные обратно, вызовет select, который вернет 1, указывая, что сокет доступен для чтения, затем вызов read вернет 0, указывая EOF / FIN. Клиент тогда выйдет. Все хорошо.

Однако идентичные приложения через SCTP вызывают проблемы. Единственное изменение было сделано с IPPROTO_TCP на IPPROTO_SCTP. Сервер разветвляется, эхо возвращает данные, потомок выходит и пожинается родителем. Клиент получает все данные, но после этого select продолжает возвращать 0 готовых дескрипторов (без добавленного времени ожидания он зависнет навсегда).

Что в мире происходит?

Вот как выглядит код для клиента:

#!/usr/bin/perl -w
use strict;
use Socket;

# forward declaration
sub logmsg;

my $remote = shift || "localhost";
my $port = 9877;

($port) = $port =~ /^(\d+)$/ or die "invalid port";

my $iaddr = inet_aton($remote) || die "no host: $remote";
my $paddr = sockaddr_in($port, $iaddr);
my $proto = getprotobyname('sctp');

my $sockfd;

socket($sockfd, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
connect($sockfd, $paddr) || die "connect: $!";

str_cli($sockfd);

exit(0);

#----- subs down here ------#

sub str_cli {
    my $sockfd = shift;

    my ($n, $buff, $stdineof, $s);
    my $rin = '';

    $stdineof = 0;
    while ( 1 ) {
        if ($stdineof == 0) {
            vec($rin, fileno(STDIN), 1) = 1;
        }
        vec($rin, fileno($sockfd), 1) = 1; 

        my $nfound = select($rin, undef, undef, 1.0);
        if ($nfound < 0) {
            next if $!{EINTR};
            die "select: $!";
        } else { print "\$nfound == $nfound\n"; }

        if (vec($rin, fileno($sockfd), 1) == 1) { # socket readable
            print "trying to read from sockfd\n";
            $n = sysread($sockfd, $buff, 1024);
            if (!defined($n) || $n < 0) {
                # resume if sysread() returned because a signal was received
                next if $!{EINTR};
                die "sysread: $!";
            } elsif($n == 0) {
                if ($stdineof == 1) { return; } # normal termination
                else { die "str_cli: server terminated prematurely"; }
            }
            writen(*STDOUT, $buff);
        }

        if (vec($rin, fileno(STDIN), 1) == 1) { # stdin readable
            $n = sysread(STDIN, $buff, 1024);
            if (!defined($n) || $n < 0) {
                # resume if sysread() returned because a signal was received
                next if $!{EINTR};
                die "sysread: $!";
            } elsif($n == 0) {
                $stdineof = 1;
                if (!defined($s = shutdown($sockfd, SHUT_WR)) 
                    || $s == 0) { die("shutdown: $!"); }
                vec($rin, fileno(STDIN), 1) = 0;
                next;
            }
            writen($sockfd, $buff);
        }
    }
}

sub writen {
    my ($connfd, $buff) = @_;
    my $nleft = length($buff);
    my $total = 0;
    my $nwritten = 0;
    while ($nleft) {
        if (($nwritten = syswrite($connfd, $buff, $nleft, $total)) <= 0) {
            # resume if syswrite() returned because a signal was received
            # 0 indicates an error in this context
            next if $!{EINTR};
            die "syswrite: $!";
        }
        $nleft -= $nwritten;
        $total += $nwritten;
    }
}

sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }

Помните, это отлично работает по протоколу TCP. Я нахожусь на Ubuntu 9.04 со всеми необходимыми пакетами sctp.

1 Ответ

3 голосов
/ 11 февраля 2010

Вы полагаетесь на полузакрытое состояние TCP, которое недоступно в SCTP.

Используя TCP, shutdown($sockfd, SHUT_WR) отправит пакет с установленным битом FIN, закрывая отправляющую сторону соединения, но по-прежнему разрешая новые данные в направлении приема. SCTP не имеет такого полузакрытого состояния, и этот вызов инициирует последовательность отключения SCTP, при которой все соединение закрывается. Дальнейшие подробности можно найти здесь .

...