2010-02-11 21 views
3

这回0有关的问题:SCTP with Multihoming as a Drop In Replacement for TCP选择在一个封闭的SCTP套接字

我有一个使用TCP运行完全正常的一个简单的回声客户机/服务器的并发应用程序。我可以将一个文件传输到客户端上的stdin,客户端将接收到所有数据,请拨select返回1,表示套接字可读,然后读取的呼叫将返回0,指示EOF/FIN。客户端然后将退出。一切都很好。

但是,SCTP上的相同应用程序会导致问题。唯一的改变是从IPPROTO_TCP到IPPROTO_SCTP。服务器分叉,echo回数据,孩子退出并由父母收回。客户端收到所有的数据,但之后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软件包。

回答

3

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

使用TCP,shutdown($sockfd, SHUT_WR)将发送FIN位设置的数据包,关闭连接的发送端,但仍然允许接收方向的新数据。 SCTP没有这种半关闭状态,并且这个调用将启动SCTP关闭序列,其中整个连接关闭。进一步的细节可以发现here

+0

那么在SCTP中有没有一种方法可以告诉套接字关闭,而不是应用程序协议级别通知? – 2010-02-13 22:12:30

+1

如果您想发送一个指示,表示您已完成发送,但仍希望保持连接以接收响应,并且不想在应用程序层执行此操作,则可以将数据发送到单独的SCTP流中。接收器可以对该流的关闭作出反应,并在不同的流中发回响应。 – mark4o 2010-02-13 22:43:05

+0

@ mark4o:您需要使用sctp特定API调用来做到这一点,对吗?有没有使用标准套接字API的方法?只是好奇在移植一个你不想引入sctp特定api调用的现有TCP应用程序时可以做些什么。 – 2010-02-14 08:23:58