【问题标题】:Select returning 0 on a closed SCTP socket选择在关闭的 SCTP 套接字上返回 0
【发布时间】:2011-01-15 15:55:31
【问题描述】:

这与问题有关:SCTP with Multihoming as a Drop In Replacement for TCP

我有一个简单的回显客户端/并发服务器应用程序,它使用 TCP 运行得非常好。我可以将文件通过管道传输到客户端上的标准输入,客户端将接收所有数据,调用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 包。

【问题讨论】:

    标签: perl sockets network-programming network-protocols sctp


    【解决方案1】:

    您依赖于 TCP 的半关闭状态,这在 SCTP 中不可用。

    使用 TCP,shutdown($sockfd, SHUT_WR) 将发送一个设置了 FIN 位的数据包,关闭连接的发送端,但仍允许接收方向的新数据。 SCTP 没有这样的半关闭状态,这个调用会发起 SCTP 关闭序列,在这个序列中整个连接都关闭了。更多详情请见here

    【讨论】:

    • 那么除了应用程序协议级别的通知之外,SCTP 中还有其他方法可以告诉套接字已关闭吗?
    • 如果你想发送一个你已经完成发送的指示,但仍然想保持连接以接收响应,并且不想在应用层这样做,你可以发送您的数据在单独的 SCTP 流中。然后,接收者可以对该流的关闭做出反应,并在不同的流中发回响应。
    • @mark4o:您需要使用 sctp 特定的 api 调用来执行此操作,对吗?有没有使用标准套接字 api 的方法?只是好奇在移植您不想在其中引入 sctp 特定 api 调用的现有 TCP 应用程序时可以做什么。
    • 如果您不想使用像sctp_sendmsg 这样的特定于sctp 的调用(它将流编号作为参数),您可以使用sendmsg 执行相同的操作。这在技术上是一个标准的套接字 api 函数,但是您传递给 sendmsg 的辅助数据将是特定于 sctp 的。
    • 我就是这么想的。我只是在回顾您提供的链接:shutdown。我的理解是它关闭了关联而不是套接字,这就是为什么 select 不会像使用 TCP 那样返回任何准备好的描述符?
    猜你喜欢
    • 2015-08-09
    • 1970-01-01
    • 2015-05-08
    • 1970-01-01
    • 2011-02-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多