- What are Sockets?
- Network Protocols
- Basic Socket Functions
- Three Example Perl Programs That Use Sockets
- Conclusion
Three Example Perl Programs That Use Sockets
The following examples use the socket functions and network protocols explained previously. Basic knowledge of Perl is required. The utilities are portable and can be used on Windows as well as the UNIX platform from the command line. These utilities should not be run in a production environment or on a machine that contains critical data, critical information, or critical systems because they do not contain error and security checks. The sole purpose of the examples here is to explain the usage of sockets in network systems.
Email Utility Using the SMTP Protocol
Objective: To be able to send email from the command line.
The client email utility works by first sending the message along with the receiver information to the SMTP server. The connection then is closed, and this message is picked up from the SMTP server by a POP3 client at the receiver's end.
The following are the generic steps to be kept in mind while coding the utility:
Find out the SMTP server hostname or address. Use this as the last argument when invoking the utility.
Create a client SMTP socket on your end through which you will carry on our conversation with the SMTP server.
Now try to establish a connection with the SMTP server.
After the connection is accepted, you need to begin sending the mail information to the server.
According to the SMTP protocol, you first identify yourself by sending a "HELO Client Name," then sender information, then receiver information, and then the message to appear at the receiver's end.
Tell the server you are finished.
Close the connection.
The utility in Listing 1 can be used on Windows as well as UNIX.
Listing 1: Email Utility Using SMTP
#!/usr/bin/perl # Author: Prashant V. Khergaonkar # Usage: mailx "Subject" "Receiver Addresses" "Message File Name" # "Sender Address" "SMTP Server" use Socket; # address of person/s you want to send mail to $mailTo = $ARGV[1]; # address of the Mail server $mailServer = $ARGV[4] || 'mailhost'; # e-mail address of the sender $mailFrom = $ARGV[3] || 'Prashant.Khergaonkar@bbh .com'; # subject of the mail $subject = $ARGV[0] || "Test"; $body = $ARGV[2] || "Testing - Please disregard e-mail. [ccc] \r\n"; # e-mail message # getprotobyname is the function used to get the number representing # TCP, If function fails use 6 $proto = getprotobyname("tcp") || 6; # getservbyname is the function used to get the number representing # SMTP, If function fails use 25 $port = getservbyname("smtp", "tcp") || 25; # gethostbyname is the function used to get the ip address using # the host name $serverAddr = gethostbyname($mailServer) || print [ccc] "gethostbyname error: $! \n"; # create the Client Socket s_SMTP which will be used to talk to # the SMTP server socket(s_SMTP,AF_INET,SOCK_STREAM,$proto) || die("socket error : [ccc] $! \n"); # pack format meaning : <unsigned short><short in network order> # <null padded 4 char string><8 null bytes> # check the operating system and assign the pack format accordingly if ( $^O =~ /Win/ ) { $packFormat = 'S n a4 x8'; # Windows 95, SunOs 4.1+ : } if ( $^O =~ /solaris/ ) { $packFormat = 'S n c4 x8'; # SunOs 5.4+ (Solaris 2) } # pack function packs the SMTP Server information according to format # specified as explained above. $s_server = pack($packFormat,AF_INET,$port,$serverAddr); # connect to the SMTP Mail Server connect(s_SMTP,$s_server) || die("connect error: $! \n"); # wait to receive an OK signal from the SMTP Server that it has accepted our connection. # and that we can begin actual information transfer recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; # Each message block ends with \r\n or \n to indicate to server we # have finished sending. # send 'HELO' to the server - this is required to indicate we # are ready to begin. send(s_SMTP,"HELO xyz.com \r\n",0); # receive the server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; # send the e-mail address of the sender(yours) to the server -- # end with end-of -line send(s_SMTP,"MAIL From: \r\n",0); # receive the server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; # use the split function to separate multiple receiver e-mail addresses # if not multiple no difference. @mT = split(' ',$mailTo); foreach $mTo (@mT) { # send the e-mail address of the receiver to the server send(s_SMTP,"RCPT To: \r\n",0); # receive the server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; } # by sending 'DATA' to the server we indicate that we are ready to # begin sending the e-mail details and message that should appear at # the receiver. send(s_SMTP,"DATA \r\n",0); # receive the server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; ########################################## # Will appear in the To text box when the receiver sees mesg send(s_SMTP,"To: $mailTo \r\n", 0); # Will appear in the From text box when the receiver sees mesg send(s_SMTP,"From: $mailFrom \r\n", 0); # Will appear in the Subject text box when he receiver sees mesg send(s_SMTP,"Subject: $subject \r\n", 0); # open the file which contains the message to be sent in mail. open(f1,"$body"); while ( $i = <f1> ) # read file a line at a time { # send the file contents line by line send(s_SMTP,"$i", 0); } ########################################## # To indicate the end of e-mail send a full-stop on a new line # followed by a new line. send(s_SMTP,"\r\n .\r\n", 0); # receive server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; # send QUIT to the server to indicate we are done and have no more # messages to send # and that the server can close the connection. send(s_SMTP,"QUIT \r\n",0); # receive server response recv(s_SMTP,$inpbuf,1000,0); print "$inpbuf \n"; # close our side of the connection shutdown(s_SMTP,2) || print "shutdown error: $! \n"; # close(s_SMTP) || print "close error: $! \n"; # close the disk file which contains the message sent over. close(f1); exit 0;
Simple Web Client Using the HTTP protocol
Objective: To request and receive a Web page from a site (Web server) the Internet.
The Web client works by establishing a connection with the Web site HTTP Web server. The request for the file is sent along with the client information to the Web site HTTP Web server. The file sent by the HTTP Web server then is received, and the connection is closed.
The following are the generic steps to be kept in mind while coding the utility:
Find out the site name or HTTP Web server hostname or IP address. Use this as the first argument when invoking the utility.
Create a client HTTP socket on your end through which you will carry on your conversation with the Web site HTTP server.
Now try to establish a connection with the HTTP server.
After your connection is accepted, you need to begin your conversation with the server.
According to the HTTP protocol, you use GET <path>/filename to request the file.
Start receiving the file bytes sent by the server, and store them in a temporary file on your machine.
After the file has been received, close your connection.
The utility in Listing 2 can be used on Windows as well as UNIX.
Listing 2: Simple Web Client Using HTTP
#!/usr/bin/perl # Author : Prashant V. Khergaonkar # Usage : webclient.pl "Site Address" #require 5.002; use Socket; # site address to connect to. $remote = $ARGV[0] || 'execonn.com'; # getprotobyname is the function used to get the number representing # TCP, if function fails use 6 $proto = getprotobyname("tcp") || 6; # getservbyname is the function used to get the number representing # HTTP, if function fails use 80 $port = getservbyname("http", "tcp") || 80; # gethostbyname is the function used to get the ip address using # the host name $serverAddr = gethostbyname($remote) || print [ccc] "gethostbyname error: $! \n"; # create the Client Socket s_CLIENT which we will use to connect # to the HTTP server socket(s_CLIENT,AF_INET,SOCK_STREAM,$proto) || [ccc] die("socket error : $! \n"); # pack format meaning : <unsigned short><short in network order> # <null padded 4 char string><8 null bytes> #check the operating system and assign the pack format accordingly if ( $^O =~ /Win/ ) { $packFormat = 'S n a4 x8'; # Windows 95, SunOs 4.1+ } if ( $^O =~ /solaris/ ) { $packFormat = 'S n c4 x8'; # SunOs 5.4+ (Solaris 2) } # pack function packs the HTTP Server information according to # format specified # as explained above. $s_server = pack($packFormat,AF_INET,$port,$serverAddr) || [ccc] die("pack error : $! \n"); # connect to the HTTP Web Server connect(s_CLIENT,$s_server) || die("connect error: $! \n"); # the request that we are going to send to the server : # here we request the server to send us the index.html file # by default most of the sites send this file when we connect to # them. Ex. http://www.sitename.com can be interpreted as # http://www.sitename.com/index.html $reqt="GET /index.html \r\n HTTP 1.0 \r\n\r\n"; # send the above request to the HTTP server send(s_CLIENT,$reqt,0) || print "send error: $! \n"; # Save the file sent over by the server in this file called # say temp.htm. # The file temp.htm should be viewed using a browser # it is the file that is requested by our client from the server. open ( f1,">temp.htm"); # recdt will contain the incoming parts of file requested by us # being sent over by the HTTP server # set recdt to a default value to get inside the loop, when the # server is done sending over the file, recdt will be empty and # the while loop terminates. $recdt=1; while ( $recdt ) { print "in \n"; recv(s_CLIENT,$recdt,100,0); print "if receive error: $! \n"; # write data received to disk file. print f1 "$recdt"; print "$recdt"; } # close our side of the connection shutdown(s_CLIENT,2) || print "shutdown error: $! \n"; #close(s_CLIENT) || print "close error: $! \n; #close the file which contains data sent over by server close(f1); exit 0;
Simple Web Server Using the HTTP Protocol
Objective: To listen for and receive clients file requests, and to send the file to the client on the Internet.
The Web server works by listening for a client request, accepting the client request, sending the file requested by the client, and listening for the next request.
The following are the generic steps to be kept in mind while coding the utility:
Create a server HTTP socket, and bind it to a port (80) on your machine.
Listen for client file requests.
Accept the client file request by creating and assigning a temporary socket to identify the client.
If the file requested by the client exists, send it using the temporary socket created specifically for that client.
Close the temporary socket.
Listen for the next client request.
The utility in Listing 3 can be used on Windows as well as UNIX.
Listing 3: Simple Web Server Using HTTP
#!/usr/bin/perl # Author : Prashant V. Khergaonkar # Usage : webserver.pl use Socket; $buffer = ' '; # getprotobyname is the function used to get the number representing # TCP, if function fails use 6 $proto = getprotobyname("tcp") || 6; # getservbyname is the function used to get the number representing # HTTP, if function fails use 80 $port = getservbyname("http", "tcp") || 80; $serverAddr = INADDR_ANY; # Create the HTTP Server Socket which we will use to handle HTTP # Client requests on. socket(s_HTTP,AF_INET,SOCK_STREAM,$proto) || die("socket error : $! \n"); # pack format meaning : <unsigned short><short in network order> # <null padded 4 char string><8 null bytes> # check the operating system and assign the pack format # accordingly if ( $^O =~ /Win/ ) { $packFormat = 'S n a4 x8'; # Windows 95, SunOs 4.1+ : } if ( $^O =~ /solaris/ ) { $packFormat = 'S n c4 x8'; # SunOs 5.4+ (Solaris 2) } # pack function packs the HTTP Server information according to format # specified as explained above. $s_server = pack($packFormat,AF_INET,$port,$serverAddr); # Associate the port number to the socket on which it will listen # for client requests # also value of serverAddr as defined above and part of # the packed information # indicates that any Client can try to establish a connection # to the server. bind(s_HTTP,$s_server) || die("bind error: $! \n"); # Here we specify the 3 client requests will be allowed simultaneously # and queued for service listen(s_HTTP,3) || die("listen error: $! \n"); # Here our server will be able to service clients one after # another i.e when one client is being serviced the others # will be queued (max two clients will be queued as# specified above in listen). $NoClient = 0; # begin infinite loop while ( 1 ) { # Wait for and accept a Client connection request and create a socket # descriptor s_CLIENT for the client accept(s_CLIENT,s_HTTP) || print "accept error: $! \n"; $NoClient= $NoClient + 1; # After a client connection is accepted, wait for and receive the # request from the client recv(s_CLIENT,$buffer,1000,0); print "recv1 error: $! \n"; print "1 -- $buffer \r\n"; # A check to see if the client request is valid can be added here. # ex. If the requested file is a valid file on our system. # if the Client request is valid send the following header to the # client to indicate to the client # that its request is valid and it should be ready to receive its # requested information. $header = "HTTP/1.0 200 OK \r\n Content-Length: 1000 \r\n [ccc] Content-Type: text/html \r\n\r\n"; send(s_CLIENT,$header,0); print "if send1 error: $! \n"; $body = " <html>You are connected</html> \r\n"; send(s_CLIENT,$body,0); print "if send2 error: $! \n"; # Depending on the server operating system, the path to the file # requested by client changes . # Here I have hard coded the file path, it should be changed # accordingly. # If the client request consists of only a "/" then we send over the # default index.html file. # If the client request consists of a path to the file, we append this # path to our default path. if ( $^O =~ /Win/ ) { # default home directory where our files are stored # on the windows server. $filename = 'c:\perl\bat'; @request = split(/\r\n/,$buffer); @req = split(/ /,$request[0]); if ($req[1] eq "/" ) { $filename = "$filename\\index.html"; #$filename = join("",$filename,'\index.html'); print "$filename \n"; } else { $filename = "$filename$req[1]"; $filename =~ s/\//\\/; print "$filename \n"; } } if ( $^O =~ /solaris/ ) { # default home directory where our files # are stored on the unix server. $filename = '/home/perl/bat/' @request = split(/\n/,$buffer); @req = split(/ /,$request[0]); if ($req[1] eq "/" ) { $filename = "$filename/index.html"; #$filename = join("",$filename,'index.html'); print "$filename \n"; } else { $filename = "$filename$req[1]"; print "$filename \n"; } } $body = $filename; # open the file requested by client open(f1,"$body"); # send over the file to the Client. while ( $i = <f1> ) { send(s_CLIENT,"$i", 0); } # close the disk file whose contents have been sent over close(f1); # close the temporary socket descriptor after servicing the # client request. shutdown(s_CLIENT,2) || print "shutdown error: $! \n"; #close(s_CLIENT) || print "close error: $! \n"; } # close our side of the connection shutdown(s_HTTP,2) || print " server shutdown error: $! \n"; exit 0;