#!/usr/bin/perl ########################################################################## # # Slightly hacked and heavily documented simple Client example from # the book Programming Perl, 1st Edition. I don't use the module support # 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 examples # 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 following # command: # # % man -s3N accept # # The -s3N tells man to look in 3N section of the Unix manual, a section # 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 right # values. This replaces the following lines in the example in the book: # # $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 first # 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 hasn't, # assigns the default port value 2345. If 'them' (the machine on which # 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 quotes # and return the output. The hostname command returns the name of the # 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 wish # 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 was # done in the Server code. Basically, if the specified port is an integer, # 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 Server # 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 allows # the parameters to 'bind' and (later) 'connect' to be smooshed together # in the correct way. For most cases this will be "cut and paste" code. # 'this' gives information about who we are; 'that' refers to the server. $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 end. 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 echoed # back by the server. But how can we both wait for input AND be receptive # to the server's output? Answer: By forking a process to accept input # from standard input (the keyboard) and send it to the server and using # 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 () { 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 () { print "Server: $_"; } }