TCP Servers with IO::Socket
As always, setting up a server is little bit more involved than running a client. The model
is that the server creates a special kind of socket that does nothing but listen on a
particular port for incoming connections. It does this by calling the IO::Socket::INET->new()
method with slightly different arguments than the client did.
- Proto
- This is which protocol to use. Like our clients, we'll still specify
"tcp"
here.
- LocalPort
- We specify a local port in the
LocalPort argument, which we didn't do for
the client. This is service name or port number for which you want to be the server.
(Under Unix, ports under 1024 are restricted to the superuser.) In our sample, we'll use
port 9000, but you can use any port that's not currently in use on your system. If you try
to use one already in used, you'll get an "Address already in use" message.
Under Unix, the netstat -a command will show which services current have
servers.
- Listen
- The
Listen parameter is set to the maximum number of pending connections we
can accept until we turn away incoming clients. Think of it as a call-waiting queue for
your telephone. The low-level Socket module has a special symbol for the system maximum,
which is SOMAXCONN.
- Reuse
- The
Reuse parameter is needed so that we restart our server manually
without waiting a few minutes to allow system buffers to clear out.
Once the generic server socket has been created using the parameters listed above, the
server then waits for a new client to connect to it. The server blocks in the accept
method, which eventually accepts a bidirectional connection from the remote client. (Make sure
to autoflush this handle to circumvent buffering.)
To add to user-friendliness, our server prompts the user for commands. Most servers don't
do this. Because of the prompt without a newline, you'll have to use the sysread
variant of the interactive client above.
This server accepts one of five different commands, sending output back to the client. Note
that unlike most network servers, this one only handles one incoming client at a time.
Multithreaded servers are covered in Chapter 6 of the Camel.
Here's the code. We'll
#!/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);
printf "[Connect from %s]\n", $hostinfo->name || $client->peerhost;
print $client "Command? ";
while ( <$client>) {
next unless /\S/; # blank line
if (/quit|exit/i) { last; }
elsif (/date|time/i) { printf $client "%s\n", scalar localtime; }
elsif (/who/i ) { print $client `who 2>&1`; }
elsif (/cookie/i ) { print $client `/usr/games/fortune 2>&1`; }
elsif (/motd/i ) { print $client `cat /etc/motd 2>&1`; }
else {
print $client "Commands: quit date who cookie motd\n";
}
} continue {
print $client "Command? ";
}
close $client;
}
|
|
|
|