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.
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? –
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
@ 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. –