in reply to Re^2: Windows TCP socket client hangs in perlipc code
in thread Windows TCP socket client hangs in perlipc code
This last is very important, because when you write client/server code, you eventually need a reliable mechanism for distinguishing between "Here's another line of text (and there will be more)", vs. "Here's the last line of text in my response".
My recommendation for handling this is to choose between two characters at the beginning of the line, one which means "last line", and one which means "not last line". Then the client code can just trim off the first character and inspect it, and continue to fetch lines from the server until the last line is fetched.
For example, use "-" to mean more text is coming, and "+" to mean that this is the last line.
Then your server code might look like this:
#!/usr/bin/perl -w use IO::Socket; use Net::hostent; # for OO version of gethostbyaddr $PORT = 9000; # pick something not in use $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $PORT, Listen => SOMAXCONN, Reuse => 1); die "can't setup server" unless $server; print "[Server $0 accepting clients]\n"; while ($client = $server->accept()) { $client->autoflush(1); print $client "-Welcome to $0; type help for command list.\n"; $hostinfo = gethostbyaddr($client->peeraddr); my $response = $hostinfo ? $hostinfo->name : $client->peerhost; printf "[Connect from %s]\n", $response; print $client "+Command?\n"; while (<$client>) { if (/quit|exit/i) { last; + } elsif (/date|time/i) { print $client "-SELECTED: date/time\n +"; } elsif (/who/i ) { print $client "-SELECTED: who\n"; + } elsif (/cookie/i ) { print $client "-SELECTED: cookie\n"; + } elsif (/motd/i ) { print $client "-SELECTED: motd\n"; + } else { print $client "-Commands: quit date who cookie motd\n"; } print $client "+Command?\n"; } close $client; }
and your client code like this:
#!/usr/bin/perl -w use strict; use IO::Socket; my ($host, $port, $kidpid, $handle, $line); unless (@ARGV == 2) { die "usage: $0 host port" } ($host, $port) = @ARGV; # create a tcp connection to the specified host and port $handle = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $host, PeerPort => $port) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # Get/display handshake message from server my $phandshake = get_from_server($handle); map { print $_, "\n" } @$phandshake; # Socket loop while (1) { # Get user input and send it to the server $line = <STDIN>; print $handle $line; # Get and display server response my $plines = get_from_server($handle); map { print "$_\n" } @$plines; } sub get_from_server { my $handle = shift; my @lines; while (1) { my $line = <$handle>; defined($line) or die "Server closed connection\n"; chomp $line; ($line =~ s/^([-+])//) or die "Invalid server response: '$lin +e'\n"; my $char = $1; push @lines, $line; last if ($char eq '+'); } return [ @lines ]; }
Note that in the client code I've abstracted out the details of parsing the text from the server into a subroutine get_from_server, which takes a single argument (the server handle), reads lines from the server until it gets the last line ('+' as the first character), or the server closes the connection. Finally, it returns a pointer to the list of lines. This makes it easier to deal with in the main program section of the client; you only have to do:
my $plines = get_from_server($handle); map { print "$_\n" } @$plines;
|
|---|