This is related to the question: SCTP with Multihoming as a replacement for replacement in TCP
I have a simple echo client / concurrent server application that works fine using TCP. I could transfer the file to stdin on the client, and the client will get all the data back, call selectwhich will return 1, indicating that the socket was readable, after which the read request will return 0, indicating EOF / FIN. Then the client will exit. All is well.
However, identical applications through SCTP cause problems. The only change was made from IPPROTO_TCP to IPPROTO_SCTP. Fork server, echo back data, the child exits and receives from the parent. The client receives all the data, but after that it selectcontinues to return 0 descriptors, ready (without a timeout, I added that it will hang forever).
What is happening in the world?
Here is the code for the client:
use strict;
use Socket;
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);
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) {
print "trying to read from sockfd\n";
$n = sysread($sockfd, $buff, 1024);
if (!defined($n) || $n < 0) {
next if $!{EINTR};
die "sysread: $!";
} elsif($n == 0) {
if ($stdineof == 1) { return; }
else { die "str_cli: server terminated prematurely"; }
}
writen(*STDOUT, $buff);
}
if (vec($rin, fileno(STDIN), 1) == 1) {
$n = sysread(STDIN, $buff, 1024);
if (!defined($n) || $n < 0) {
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) {
next if $!{EINTR};
die "syswrite: $!";
}
$nleft -= $nwritten;
$total += $nwritten;
}
}
sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }
Remember that this works great on TCP. I'm on Ubuntu 9.04 with all the necessary sctp packages.