Sure, it taught me alot. To get away from your obsession on readline, try this slightly modified version. I can kill the client's xterm, before I send any newline, and the server stays responsive to new and other connections.
Server:
#!/usr/bin/perl
######################################################################
+####
#
# Slightly hacked and heavily documented Server example from
# the book Programming Perl, 1st Edition. I don't use the module sup
+port
# for sockets much because I want the details to be out in the open
# (primarily so people can take their Perl sockets knowledge and move
+to
# other languages easily).
#
# I've rearranged some of the source to put related things side-by-sid
+e,
# too (The 'pack' and 'bind', for example).
#
# Hacking and documentation by Prof. Golden G. Richard III, Dept. of
# Computer Science, University of New Orleans, April 1996-March 1998.
#
# Command line arguments expected are: port
#
######################################################################
+####
#
# Preliminary notes:
#
# You see the $! thing all over the place. Perl special variables beg
+in
# with $ and there are a LOT of them! $! stands for "the current sys
+tem
# error". So if something goes wrong with a system call, $! provides
+a
# diagnosis. If you've ever used 'errno' or 'perror' in C or C++, thi
+s
# is similar.
# Perl socket stuff is based on, and very similar to, C socket stuff.
+
# The Unix man pages for the various socket system calls such as
# 'listen', 'bind', etc. may be very useful to you. To get
# information on a particular call, such as 'accept', use the followin
+g
# command:
#
# % man -s3N accept
#
# The -s3N tells man to look in 3N section of the Unix manual, a secti
+on
# that's not searched by default.
#
# For the assignments, use port addresses in the range 5000-6000.
# If you get an "address already in use" error, try a different port.
#
######################################################################
+####
# In Perl 5 and above, the Socket module contains Perl definitions for
+
# the stuff in the C include file "/usr/include/sys/socket.h".
# Rather than manually poking through that include file to get values
# for each architecture, a "use Socket" assures that you'll get the ri
+ght
# values. This replaces the following lines in the example in the boo
+k:
#
# $AF_INET = 2;
# $SOCK_STREAM = 1;
#
# which turn out to be wrong for Solaris, anyway
# ($SOCK_STREAM should be 2)
use Socket;
# Sucks in the command line arguments (resident in the array variable
+ARGV)
# and assigns them to the variables listed in (). In this case, the f
+irst
# command line argument is the port [more on this later] and it's
# placed into the variable 'port'.
($port) = @ARGV;
# This checks to see if 'port' has been assigned a value and if it has
+n't,
# assigns the default port value 2345
$port = 2345 unless $port;
# Looks up important information related to the network protocol you w
+ish
# to use. 'tcp' is a connection-oriented protocol. Look in
# /etc/protocols for examples of others. Don't change this unless yo
+u
# know what you're doing.
($name, $aliases, $protocol) = getprotobyname('tcp');
# The following line illustrates both how terse Perl can be AND how co
+ol
# Perl can be. $port is the port the user specified either on the
# command line or by default. =~ is the Perl regular expression bind
+ing
# operator and !~ is the negation of =~. Regular expressions are
# enclosed between / / characters in Perl. \d matches a single digit
+,
# \d+ matches a string of 1 or more digits, ^ specifies "beginning of
# string", and $ specifies "end of string". /^\d+$/ as a regular
# expression, therefore, matches integers of arbitrary length.
# SO... The following line says: "If the port contains any non-digi
+t
# characters, look up the port number associated with the symbolic
# name specified by the user. The lookup is done against /etc/servic
+es.
if ($port !~ /^\d+$/) {
($name, $aliases, $port) = getservbyport($port, 'tcp');
}
# Let the user know what port we're listening on, just in case (s)he
# accidentally typed an incorrect port.
print "Listening on port $port...\n";
# 'socket' creates one endpoint for a communication link (think of it
+as
# creating a telephone. Later, someone else will create another teleph
+one
# and wires will be attached between them). The S parameter is the h
+andle
# associated with the created communication endpoint. AF_INET speci
+fies
# that we're talking using ports. AF_UNIX would specify that we'd be
# communicating through special files created in the filesystem. This
# is very attractive because then you can do away with the port number
+
# business, but unfortunately AF_UNIX sockets only work on the same
# machine. SOCK_STREAM sockets communicate using streams of characters
+.
# Another possibility is unreliable datagram communication using
# SOCK_DGRAM. Just stick with the parameters used here unless you
# know what you're doing, because there are other implications you nee
+d
# to understand for datagram communication.
# In case you're wondering, the AF_INET and SOCK_STREAM symbols are
# provided by the 'use Socket;' statement at the top of the file.
# You do NOT want to put $'s in front of these symbols.
# The 'die' causes execution to terminate with an error message ( whic
+h is
# stored in $_) if the 'socket' call fails.
socket(S,AF_INET,SOCK_STREAM,$protocol) || die "socket : $!";
# so we can restart our server quickly
# The setsockopt function used in the Solution allows you to
# avoid waiting two minutes after killing your server before you
# restart it again (valuable in testing)
# setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, 1);
setsockopt(S, SOL_SOCKET, SO_REUSEADDR, 1);
# The 'bind' hooks your phone to the port number that was specified.
# Think of all the ports as a telephone switchboard. 'bind' requires
+its
# parameters to be in a C structure format. 'pack' smooshes things
# together into a form that 'bind' can stomach. The $sockaddr thing b
+elow
# says "An unsigned short, followed by a short in 'network order',
# followed by a null-padded 4 character string, followed by 8 null
# bytes." It's magic. Don't worry too much about it.
$sockaddr = 'S n a4 x8';
$this = pack($sockaddr, AF_INET, $port, "\0\0\0\0");
bind(S, $this) || die "bind : $!";
# The following arranges to queue up as many as 10 clients until we
# have a chance to service them. If more than 10 clients "get in line
+",
# the excess may receive "connection refused" errors.
listen(S,10) || die "listen: $!";
# Select S temporarily as the default output channel, turn on
# autoflushing, then select standard output again.
select(S);
$| = 1;
select(STDOUT);
# Create connections as clients "arrive". $con maintains the connecti
+on
# number of the last client
for ($con = 1; ; $con++) {
# Let the user know we're waiting for a connection...
printf("Waiting for connection %d....\n", $con);
# 'accept' blocks until it notices that a connection has been mad
+e
# to our socket S. When this occurs, the incoming connection is
# actually attached to the socket NS rather than S, thus leaving
# S free for other incoming connections.
# One more time, said another way:
#
# When we hear the the telephone S ringing, we quickly transfer
# the call to a different phone (NS) to accept the call.
# This allows S to stay free for the next call. Make sense?
# The value that's returned (in $addr) gives some information abo
+ut
# the address of the caller.
($addr = accept(NS,S)) || die $!;
# Temporarily set default output to the handle NS so...
select(NS);
# ...so we can set autoflushing. Setting $| to a non-zero value
+causes
# output to the currently selected output channel to be immediate
+ly
# flushed.
$| = 1;
# Set default output back to the standard output channel
select(STDOUT);
# The 'fork' creates a child process to handle this client.
# Remember that 'fork' returns 0 for the child and a positive num
+ber
# for the parent.
if (($child = fork()) == 0) {
# We're the child...
# unpack the information returned by 'accept' to get some
# (readable) information about the client we're serving and
# print it to standard output
($af,$port, $inetaddr) = unpack($sockaddr, $addr);
@inetaddr = unpack('C4', $inetaddr);
print "Serving connection $con @ Internet address @inetaddr
+\n";
# NS is the handle for the socket we're listening to;
# it's connected to the current client.
# <NS> reads and returns the next line of input from the
# handle NS. The following while loop form is a special P
+erl
# construct that reads input from the handle NS line by lin
+e
# until the end of file is reached. Each line is placed i
+nto
# the special Perl variable $_ (the Perl "default value"
# variable).
while (<NS>) {
# output stuff from client here and...
print "Received from client $con: $_";
# ...and echo it back to the client, too.
print NS "Server $con: $_";
}
# Close the socket connection when the client goes away
close(NS);
# The forked server dies here
print "Client went away. Forked server $con exiting...\n";
exit;
}
# this is where the parent returns; all we do is close the socket
# connection (it's being handled by the child we fork'ed) and the
+n
# enter another iteration of the big for loop.
close(NS);
}
Client:
#!/usr/bin/perl
######################################################################
+####
#
# Slightly hacked and heavily documented simple Client example from
# the book Programming Perl, 1st Edition. I don't use the module suppo
+rt
# for sockets much because I want the details to be out in the open
# (primarily so people can take their Perl sockets knowledge and move
+to
# other languages easily).
#
# Some things are done a little differently than in the server example
+s
# to give you a little more experience with Perl constructs.
#
# This client accepts input from standard input, sends the input to a
# server, and sends responses from the server to standard output.
#
# Hacking and documentation by Prof. Golden G. Richard III, Dept. of
# Computer Science, University of New Orleans, April 1996-March 1998.
#
# Command line arguments expected are: serverIPaddress, port
#
######################################################################
+####
#
# Perl socket stuff is based on, and very similar to, C socket stuff.
+
# The Unix man pages for the various socket system calls such as
# 'listen', 'bind', etc. may be very useful to you. To get
# information on a particular call, such as 'accept', use the followin
+g
# command:
#
# % man -s3N accept
#
# The -s3N tells man to look in 3N section of the Unix manual, a secti
+on
# that's not searched by default.
#
# Use port addresses in the range 5000-6000 for the assignments.
# If you get an "address already in use" error, try a different port.
#
######################################################################
+####
# In Perl 5 and above, the Socket module contains Perl definitions for
+
# the stuff in the C include file "/usr/include/sys/socket.h".
# Rather than manually poking through that include file to get values
# for each architecture, a "use Socket" assures that you'll get the ri
+ght
# values. This replaces the following lines in the example in the boo
+k:
#
# $AF_INET = 2;
# $SOCK_STREAM = 1;
#
# which turn out to be wrong for Solaris, anyway...
# ($SOCK_STREAM should be 2)
use Socket;
# Sucks in the command line arguments (resident in the array variable
+ARGV)
# and assigns them to the variables listed in (). In this case, the f
+irst
# is the IP address of the machine on which the server is
# (hopefully) running. The second is the port on which the server is
# listening.
($them,$port) = @ARGV;
# This checks to see if 'port' has been assigned a value and if it has
+n't,
# assigns the default port value 2345. If 'them' (the machine on whi
+ch
# the server is running) hasn't been assigned a value, 'localhost' is
# assumed. This means that the server is on the same machine as the
# client (us).
$port = 2345 unless $port;
$them = 'localhost' unless $them;
# This sets up a signal handler to kill the child we'll create later
# in the event that we die
$SIG{'Int'} = 'dokill';
# This is a subprogram that does the killing. The variable $child is
# initially undefined, so we won't try to kill a non-existant child.
# Later it will be the PID of the child we create.
sub dokill {
kill 9, $child if $child;
}
# Strings in back quotes like `ls` run the Unix command inside the quo
+tes
# and return the output. The hostname command returns the name of th
+e
# machine we're running on. 'chop' removes the newline character in
+the
# output from the hostname command.
chop($hostname = `hostname`);
# Looks up important information related to the network protocol you w
+ish
# to use. 'tcp' is a connection-oriented protocol. Look in
# /etc/protocols for examples of others. Do NOT change this unless you
# know what you're doing.
($name, $aliases, $proto) = getprotobyname('tcp');
# This is a slightly different way of doing the service lookup that wa
+s
# done in the Server code. Basically, if the specified port is an int
+eger,
# the port number lookup isn't done, otherwise it is.
($name, $aliases, $port) = getservbyname($port,'tcp')
unless $port =~ /^\d+$/;
# Let the user know what port we're using and where the server
# is expected to be, just in case (s)he accidentally typed an
# incorrect port or machine name.
print "Using port $port to connect to server on host $them...\n";
# This looks up numeric IP address information corresponding to the
# hostname for the current machine.
($name,$aliases,$type,$len,$thisaddr) = gethostbyname($hostname);
# This looks up numeric IP address information corresponding to the
# hostname where the server is expected to be running.
($name, $aliases,$type,$len,$thataddr) = gethostbyname($them);
# Create one endpoint of the communication link (our end). See the Se
+rver
# code for a more complete explanation of what 'socket' does. If we
# fail to create a socket, 'die' causes execution to terminate and
# display the reason ( stored in $! ) for the failure.
# Remember that AF_INET and SOCK_STREAM are symbols provided by the
# "use Socket;" line. Don't put $ in front of them!!
if (socket(S,AF_INET, SOCK_STREAM, $proto)) {
print "Socket creation succeeded.\n";
}
else {
die $!;
}
# 'bind' connects our socket (just created) to the specified port. As
# in the Server code, the $sockaddr thing is just some magic that allo
+ws
# the parameters to 'bind' and (later) 'connect' to be smooshed togeth
+er
# in the correct way. For most cases this will be "cut and paste" cod
+e.
# 'this' gives information about who we are; 'that' refers to the serv
+er.
$sockaddr = 'S n a4 x8';
$this = pack($sockaddr, AF_INET, 0, $thisaddr);
$that = pack($sockaddr, AF_INET, $port, $thataddr);
if (bind(S, $this)) {
print "Bind succeeded.\n";
}
else {
die $!;
}
# This is similar to the 'accept' in the server, except the server is
# waiting for a call and we're actively initiating the conversation.
# 'connect' connects our socket to the server's socket on the other en
+d.
if (connect(S, $that)) {
print "Connect succeeded.\n";
}
else {
die $!;
}
# Force our socket to flush output immediately after a print
select(S);
$| = 1;
# Make standard output the default again
select(STDOUT);
# The interaction we want is to type lines of input and have them echo
+ed
# back by the server. But how can we both wait for input AND be recep
+tive
# to the server's output? Answer: By forking a process to accept in
+put
# from standard input (the keyboard) and send it to the server and usi
+ng
# our current process to receive and display input from the server.
if ($child = fork) {
# We're the parent. Read lines of input from the standard input and
# send them to the server until end of file is seen.
while (<STDIN>) {
print S;
}
# Sleep for 3 seconds then...
sleep 3;
#...then kill ourselves and the child
do dokill();
}
else {
# We're the child. Read lines of input from the server over the
# socket S and output them. Stop if end of file is seen.
while (<S>) {
print "Server: $_";
}
}