2010-02-11 6 views
3

Ceci est lié à la question: SCTP with Multihoming as a Drop In Replacement for TCPretour 0 sur une socket SCTP fermée

J'ai un client simple écho/app serveur concurrent qui a fonctionné parfaitement bien en utilisant TCP. Je pourrais diriger un fichier à stdin sur le client et le client recevrait toutes les données en arrière, appelez select qui retournerait 1 indiquant que le socket était lisible, alors l'appel à lire retournerait 0 indiquant EOF/FIN. Le client quitterait alors. Tout est bon. Toutefois, les applications identiques sur SCTP posent des problèmes. Le seul changement apporté était de IPPROTO_TCP à IPPROTO_SCTP. Le serveur fige, renvoie les données, l'enfant quitte et est récolté par le parent. Le client reçoit toutes les données, mais par la suite, select ne cesse de renvoyer 0 descripteurs prêts (sans le délai, j'ai ajouté qu'il serait suspendu pour toujours).

Que se passe-t-il dans le monde?

Voici ce que le code pour le client ressemble à:

#!/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" } 

Rappelez-vous, cela fonctionne parfaitement sur TCP. Je suis sur Ubuntu 9.04 avec tous les paquets sctp nécessaires installés.

Répondre

3

Vous comptez sur l'état semi-fermé de TCP, qui n'est pas disponible dans SCTP. En utilisant TCP, shutdown($sockfd, SHUT_WR) enverra un paquet avec l'ensemble de bits FIN, fermant le côté d'envoi de la connexion tout en permettant de nouvelles données dans la direction de réception. SCTP n'a pas un tel état semi-fermé, et cet appel lancera la séquence d'arrêt SCTP, dans laquelle la connexion entière est fermée. Plus de détails peuvent être trouvés here.

+0

Y a-t-il un moyen dans SCTP de dire qu'une socket est fermée autre que les notifications de niveau de protocole d'application? –

+1

Si vous souhaitez envoyer une indication indiquant que vous avez fini d'envoyer, mais que vous souhaitez conserver la connexion pour recevoir une réponse et que vous ne souhaitez pas effectuer cette opération sur la couche d'application, vous pouvez envoyer vos données dans un flux SCTP distinct . Le récepteur peut alors réagir à la fermeture de ce flux et renvoyer une réponse dans un flux différent. – mark4o

+0

@ mark4o: Vous devez utiliser les appels api spécifiques à sctp pour cela, n'est-ce pas? Y at-il un moyen d'utiliser les sockets standard api? Juste curieux de savoir ce qui peut être fait lors du portage d'une application TCP existante dans laquelle vous ne voulez pas introduire les appels API spécifiques à sctp. –

Questions connexes