Online Documentation Server
 ПОИСК
ods.com.ua Web
 КАТЕГОРИИ
Home
Programming
Net technology
Unixes
Security
RFC, HOWTO
Web technology
Data bases
Other docs

 


 ПОДПИСКА

 О КОПИРАЙТАХ
Вся предоставленная на этом сервере информация собрана нами из разных источников. Если Вам кажется, что публикация каких-то документов нарушает чьи-либо авторские права, сообщите нам об этом.




Chapter 12

Using Sockets


CONTENTS


Perl offers a host of functions for accessing the socket-based interprocess communication facilities. The system facilities on UNIX systems are available to the Perl programmer and, thus, can be called directly from Perl scripts. Given the information in this chapter, you should be able to write your own client/server Perl applications using sockets.

A Very Brief Introduction to Sockets

Perl offers a host of functions for accessing the socket functions on a UNIX-based system. It's essential to cover some of the important features of interprocess communications in order to understand how the model fits in with Perl. Given the limited amount of space in a chapter, I cannot hope to possibly cover all the bases for socket programming. However, I will cover the basics, and you should have enough information from this chapter to develop your own client/server model using Perl.

The absolutely best reference to doing anything with network programming is UNIX Network Programming by W. Richard Stevens (Prentice Hall, ISBN 0-13-949876-1). The book provides programs and C code samples in excruciating detail that cover all aspects of network programming. BSD UNIX gives many ways to open, use, and close sockets, and this book covers them all. The examples presented in this chapter are derived from the C code in this book. Perl is great in that often you can do a line-by-line mapping of cookbook socket and network function calls in C to equivalent calls in Perl. (Someone ought to write an interpreter!) Please refer to Stevens's book for a more detailed discussion on internetworking with sockets.

In general, socket programming is based on the client/server model. A client is a program that makes requests to the server to get responses. A server is simply another program that responds to these requests. The server either can reside on the same computer as the client or can be a computer somewhere on a connecting network. Sending requests and receiving replies to transfer the data between clients and servers is the protocol with which they communicate.

A common protocol in the UNIX world is the TCP/IP protocol. The Internet Protocol (IP) handles the transfer of data from one computer to another over a network. The Transport Control Protocol (TCP) offers a set of reliability and connection functions that IP does not offer. Messages sent via TCP/IP from one computer are acknowledged by the other computer. The acknowledgment of sent messages increases reliability, but at the cost of efficiency. The User Datagram Protocol (UDP) is like TCP in sending messages, but has no acknowledge feature as such. This makes UDP faster when receiving acknowledgments is not as high of a priority as sending acknowledgments back. UDP tends to be less reliable than TCP because the sender UDP does not have a guarantee that the sent message even made it to the remote site. Think of UDP as the regular U.S. Mail service and TCP/IP as a registered letter service. Although the U.S. Mail is generally very reliable, a sender does not really know if the recipient did indeed receive the letter. As far as you are concerned, the letter got to its destination, and if it didn't the recipient will request another. This is similar to UDP. Send a message, and if it doesn't make it over to the destination-no problem-the recipient will ask again. When sending important documents though, you would most likely want to get a confirmation from the recipient. In this case, you'd normally use a registered letter service, which will return a signed receipt. The signed receipt is your "acknowledgment" in TCP/IP.

Most applications using TCP or UDP have a port number that they talk on to get the service they want. A machine assigns one unique port number to an application. Port numbers are standardized enough to identify the type of service being used. On UNIX systems, the file /etc/services maintains a list of services offered on each port. Port numbers between 1 and 255 are reserved for standard, well-known applications. There are well-known port numbers that everyone recognizes: for example, port 80 for the World Wide Web's server daemons, the nameserver on port 42, sendmail at port 25, and so on. In all cases, avoid using socket 0, since it's interpreted differently on different systems.

Two computers talk to each other via a network circuit. Each circuit is uniquely identified by a combination of two numbers called a socket. Basically a socket is the IP address of the machine plus the port number used by the TCP software.

There are two ways of defining the address: If two processes talking to each other are on the same machine, the "family" of protocols is referred to as AF_UNIX. If the communicators are on different machines, this is referred to as the AF_INET family. In the AF_UNIX family, sockets are assigned a pathname in the directory tree. In the AF_INET family, they are assigned a port number and application number. Using AF_INET, you can talk to processes on the same machine, but AF_UNIX is reserved for the same machine.

There are two types of sockets about which you should know:

  • SOCK_STREAM. This type of socket utilizes TCP and provides a reliable way of transferring data across a network. The SOCK_STREAM process requires the establishment of a connection before the transfer of data and the disestablishment of the connection after all transfers are complete. While connected, the client and server can send data to and receive data from each other. Thus, SOCK_STREAM is a connection-oriented protocol.
  • SOCK_DGRAM. This type of socket utilizes UDP and provides a faster way of transmitting than TCP, but does not require the establishment of a connection for it to be able to transfer data. The destination socket may not even exist. The SOCK_DGRAM is referred to as a connectionless protocol.

There is a socket on both the sending and receiving machine. Clients send on their sockets, and servers listen on their sockets and accept connections when necessary. The IP address of each machine is guaranteed to be unique by design, and the port numbers are unique to each machine. This implies the socket numbers, which are a combination of these two unique numbers, will also be unique across the network. This allows two applications to communicate using unique socket numbers.

With Perl, it's possible to get access to these socket and network functions. Most of this chapter has a UNIX slant to it. On NT machines, you'll be dealing with the Remote Access Server and WinSock under Windows. Please refer to the technical notes from Microsoft for more information on WinSock programming.

Perl Functions for Working with Protocols

The protocols available on your UNIX system are located in the /etc/protocols file. You have to use three functions to read this file. The function setprotoent() starts the listing process. The Perl function getprotoent() reads one line from the /etc/protocols file and lists it for you. Successive calls to the function read successive lines. Finally, a call to endprotoent() stops the listening process. A simple way to have all the protocols available to your Perl script is to use the script shown in Listing 12.1.


Listing 12.1. Showing available protocols.
1 #!/usr/bin/perl
2 #
3 # List all the protocols in this machine
4 #
5 setprotoent(1);
6 while (($name, $aliases, $protonum) = getprotoent) {
7   print " Name=$name, Aliases=$aliases, Protocol=$protonum \n";
8 }
9 endprotoent;

The output should be similar to what is shown here:

Name=ip, Aliases=IP, Protocol=0
Name=icmp, Aliases=ICMP, Protocol=1
Name=igmp, Aliases=IGMP, Protocol=2
Name=ggp, Aliases=GGP, Protocol=3
Name=tcp, Aliases=TCP, Protocol=6
Name=pup, Aliases=PUP, Protocol=12
Name=udp, Aliases=UDP, Protocol=17
Name=idp, Aliases=IDP, Protocol=22
Name=raw, Aliases=RAW, Protocol=255

To keep the file open between successive calls to the getprotoent() call, you should call the setprotoent() function with a nonzero parameter. To stop querying the file, use the endprotoent() call.

To determine whether you have a specific protocol present, you can use the system call getprotobyname or getprotobynumber. A return value of NULL indicates that the protocol is not there. The name passed to the function is not case-sensitive. Therefore, to list the names, aliases, and the protocol number for TCP, you can use this:

if (($name, $aliases, $protonum) = getprotobyname('tcp')) {
   print "\n Name=$name, Aliases=$aliases, Protocol=$protonum";
}
print "\n"

A comparable set of calls is available for determining what services are available for your machine. This call queries the /etc/services file. Listing 12.2 illustrates how to use these calls. The setservent call with a nonzero file rewinds the index into the services file for you, the getservent gets the four items in the service entry, and the endservent call terminates the lookup. The output from this file can be a bit lengthy and is shown in Listing 12.2 starting at line 15.

In Listing 12.2, lines 1 and 2 clear the screen and show the output of the showme.pl file with the script in it. At line 13, we execute this script. Your output may be different than the one shown in Listing 12.2 depending on what services you have installed on your system.


Listing 12.2. Listing server services.
 1 $ clear
 2 $ cat showme.pl
 3 #!/usr/bin/perl
 4 setservent(1);
 5 printf "%15s %15s %4s %15s\n",
 6   "Name","Aliases","Port","Protocol";
 7 while(($nm,$al,$pt,$pr) = getservent) {
 8   # print "Name=$nm, Aliases=$al, Port=$pt, $Protocol=$pr\n";
 9   printf "%15s %15s %4d %15s\n", $nm,$al,$pt,$pr;
 10 }
 11 endservent;
 12 $
 13 $
 14 $ showme.pl
 15      Name     Aliases     Port     Protocol
 16     tcpmux               1       tcp
 17      echo               7       tcp
 18      echo               7       udp
 19     discard    sink null       9       tcp
 20     discard    sink null       9       udp
 21     systat      users      11       tcp
 22     daytime              13        tcp
 23     daytime              13        udp
 24     netstat              15        tcp
 25      qotd      quote      17       tcp
 26       msp              18       tcp
 27       msp              18       udp
 28     chargen  ttytst source      19       tcp
 29     chargen  ttytst source      19       udp
 30       ftp              21       tcp
 31     telnet              23        tcp
 32      smtp      mail      25  &nbs p;     tcp
 33      time    timserver      37       tcp
 34      time    timserver      37       udp
 35       rlp    resource      39       udp
 36   nameserver      name      42    &nbs p;   tcp
 37      whois     nicname      43       tcp
 38     domain   nameserver      53     &n bsp;  tcp
 39     domain   nameserver      53     &n bsp;  udp
 40       mtp              57       tcp
 41     bootps              67        tcp
 42     bootps              67        udp
 43     bootpc              68        tcp
 44     bootpc              68        udp
 45      tftp              69&n bsp;       udp
 46     gopher              70        tcp
 47     gopher              70        udp
 48       rje     netrjs      77       tcp
 49     finger              79        tcp
 50       www      http      80       tcp
 51       www              80       udp
 52      link     ttylink      87   & nbsp;    tcp
 53    kerberos      krb5      88         tcp
 54    kerberos              88  &nbs p;     udp
 55     supdup              95        tcp
 56    hostnames    hostname      101       tcp
 57    iso-tsap      tsap      102       tcp
 58    csnet-ns     cso-ns      105       tcp
 59    csnet-ns     cso-ns      105       udp
 60     rtelnet              107  &nbs p;     tcp
 61     rtelnet              107  &nbs p;     udp
 62      pop2   postoffice      109       tcp
 63      pop2              109 & nbsp;      udp
 64      pop3              110 & nbsp;      tcp
 65      pop3              110 & nbsp;      udp
 66     sunrpc              111        tcp
 67     sunrpc              111        udp
 68      auth tap ident authentication 113       tcp
 69      sftp              115 & nbsp;      tcp
 70    uucp-path              117       tcp
 71      nntp  readnews untp      119       tcp
 72       ntp              123       tcp
 73       ntp              123       udp
 74   netbios-ns              137   & nbsp;    tcp
 75   netbios-ns              137   & nbsp;    udp
 76   netbios-dgm              138       tcp
 77   netbios-dgm              138       udp
 78   netbios-ssn              139       tcp
 79   netbios-ssn              139       udp
 80      imap2              143       tcp
 81      imap2              143       udp
 82      snmp              161 & nbsp;      udp
 83    snmp-trap    snmptrap      162       udp
 84    cmip-man              163  &nb sp;     tcp
 85    cmip-man              163  &nb sp;     udp
 86   cmip-agent              164   & nbsp;    tcp
 87   cmip-agent              164   & nbsp;    udp
 88      xdmcp              177       tcp
 89      xdmcp              177       udp
 90    nextstep NeXTStep NextStep     178       tcp
 91    nextstep NeXTStep NextStep     178       udp
 92       bgp              179       tcp
 93       bgp              179       udp
 94    prospero              191  &nb sp;     tcp
 95    prospero              191  &nb sp;     udp
 96       irc              194       tcp
 97       irc              194       udp
 98      smux              199 & nbsp;      tcp
 99      smux              199 & nbsp;      udp
100     at-rtmp              201  &nb sp;     tcp
101     at-rtmp              201  &nb sp;     udp
102     at-nbp              202  &nbs p;     tcp
103     at-nbp              202  &nbs p;     udp
104     at-echo              204  &nb sp;     tcp
105     at-echo              204  &nb sp;     udp
106     at-zis              206  &nbs p;     tcp
107     at-zis              206  &nbs p;     udp
108      z3950      wais      210       tcp
109      z3950      wais      210       udp
110       ipx              213       tcp
111       ipx              213       udp
112      imap3              220       tcp
113      imap3              220       udp
114    ulistserv              372       tcp
115    ulistserv              372       udp
116      exec              512        tcp
117      biff     comsat      512       udp
118      login              513       tcp
119       who      whod      513       udp
120      shell       cmd      514       tcp
121     syslog              514  &nbs p;     udp
122     printer     spooler      515       tcp
123      talk              517        udp
124      ntalk              518       udp
125      route  router routed      520       udp
126      timed   timeserver      525       udp
127      tempo     newdate      526       tcp
128     courier       rpc      530       tcp
129   conference      chat      531       tcp
130     netnews    readnews      532       tcp
131     netwall              533  &nb sp;     udp
132      uucp      uucpd      540       tcp
133    remotefs rfs_server rfs      556       tcp
134     klogin              543  &nbs p;     tcp
135     kshell              544  &nbs p;     tcp
136  kerberos-adm              749    &nbs p;   tcp
137     webster              765  &nb sp;     tcp
138     webster              765  &nb sp;     udp
139   ingreslock             1524        tcp
140   ingreslock             1524        udp
141   prospero-np             1525       tcp
142   prospero-np             1525       udp
143       rfe             5002       tcp
144       rfe             5002       udp
145    krbupdate      kreg      760       tcp
146     kpasswd      kpwd      761       tcp
147     eklogin             2105   &n bsp;    tcp
148   supfilesrv              871         tcp
149   supfiledbg             1127        tcp

Tip
The gethostent function was not implemented as of Perl 5.002.

Perl also lets you look at the host name by address in your /etc/hosts file with the gethostbyaddr call. This function takes two parameters, the address to look up and the value of AF_INET. On most systems, this value is set to 2 but can be looked up in the /usr/include/sys/socket.h file. The gethostbyname("hostname") function returns the same values as the gethostbyaddr() call. The parameter passed into the function is the name of the host being looked up. Listing 12.3 illustrates how to do this.

In the program shown in Listing 12.3, the code in Line 4 gets the host name and alias given the address 204.251.103.2. You would use a different address, of course, because the address shown here is specific to my machine. Lines 6 through 10 print the components of the information you get back from the gethostbyaddr function call. Also, in lines 12 and 13, you can get the same information back using the node name instead of an IP address. Lines 14 through 19 print these values.


Listing 12.3. Sample listing to show usage of gethostbyname and gethostbyaddr.
 1 #!/usr/bin/perl
 2
 3 $addr = pack('C4',204,251,103,2);
 4 ($name,$alias,$atype,$len,@addrs) = gethostbyaddr($addr,2);
 5 ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
 6 print "Name : $name \n";
 7 print "Alias: $alias \n";
 8 print "Type : $atype \n";
 9 print "Len : $len \n";
10 print "Node : $a.$b.$c.$d \n";
11
12 $name = "www.ikra.com";
13 ($name,$alias,$atype,$len,@addrs) = gethostbyname($name);
14 ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
15 print "Name : $name \n";
16 print "Alias: $alias \n";
17 print "Type : $atype \n";
18 print "Len : $len \n";
19 print "Node : $a.$b.$c.$d \n";

Note
Note the use of the number 2 in the call to gethostbyaddr in Listing 12.3. This should be the constant $AF_INET.

Socket Primitives

Enough already about getting information on your system. Let's see what socket functions are available to you. Depending on your site and what extensions you have for Perl, you may have more functions available. Check the man pages for socket for more information. Here are the most common ones you'll use:

  • socket()-Creates a socket
  • bind()-Binds a process to a socket
  • accept()-Accepts an incoming request for connection
  • listen()-Use for servers to begin listening for connections to a socket
  • connect()-Use for clients to connect to a server
  • read()-Reads binary data from a socket
  • write()-Writes binary data to a socket

I cover these functions in the following sections. However, there are some constants that must be defined before I continue. These constants are used in all function calls and scripts in this chapter. Feel free to change them to reflect your own system's peculiarities. Here's a list of the constants:

  • $AF_UNIX = 1. You'll be working with the AF_UNIX family of protocols.
  • $SOCK_STR = 1. When you work with UDP, set this variable to 2. If you will work with TCP/IP only, set it to 1.
  • $PROTOCOL = 0. The default protocol for all our examples is the one you will most probably wind up using anyway-IP. See man pages for protocols for more information about what other types are available.

socket()

The socket() system call creates a socket for the client or the server. The socket function is defined as this:

socket(SOCKET_HANDLE, $FAMILY, $TYPE, $PROTOCOL);

The return value from this function is NULL, and if there was an error, you should check the $! for the type of error message. The call to open a socket looks like this:

socket(MY_HANDLE, $AF_UNIX, $STREAMS, $PROTOCOL) ||
   die "\nCannot open socket: $!\n";
print "\nSocket successfully opened\n";

It's a good idea to unlink any existing file names for previously opened sockets with the unlink call:

unlink "$my_tst_srvr" || die "\n$O: No permissions";

You'll use the socket descriptor MY_HANDLE to refer to this socket in all subsequent network function calls in your program. Sockets are created without a name. Clients use the name of the socket in order to read or write to it. This is where the bind function comes in.

The bind() System Call

The bind() system call assigns a name to an unnamed socket:

bind(SOCKET_HANDLE, $nameAsAString);
bind(SOCKET_HANDLE, $sockaddr);

The first item is the socket descriptor you just created. The second parameter is the name that refers to this socket if you are using AF_UNIX or its address if you are using AF_INET.

The call to bind using AF_UNIX looks like this:

bind(MY_HANDLE,"./mysocket") || die "Cannot bind $!\n";

In AF_INET, it looks like this:

$port = 6666
$AF_INET=2;   # Use AF_INET instead of AF_UNIX
($name,$alias,$atype,$len,$addr)=gethostbyname(`uname -n`);
$my_ip_addr = pack(S n C4 x8,$AF_INET,$STREAMS,$port,$addr);
bind(MY_HANDLE,$my_ip_addr) || die "Cannot bind $!\n";

The parameters' pack() function probably needs some explanation. The pack() function takes two parameters: a list of formats to use and a list of values to pack into one continuous stream of bytes. In our case, the bind() call expects a sockaddr structure of the following form in a C structure:

{
unsigned short family;
int network;
long   address;
char nullbytes[8];
   }

The first parameter to the pack instruction can take the values listed in Table 12.1. Check the man pages for the pack instruction for more details. You had the pack instruction create the socket address structure for you. Therefore, the script uses S n C4 x8 to pack a signed short, followed by an integer in network order, four unsigned characters, and eight NULL characters to get this call:

pack(S n C4 x8,$AF_INET,$STREAMS,$port,$addr);

Table 12.1. The types of packing supported by pack().
Character
Type of Packing
@
Null fill to absolute position
a
An ASCII string, padded with NULLs
A
An ASCII string, padded with spaces
b
A bit string in ascending bit order
B
A bit string in descending bit order
c
A signed char value
C
An unsigned char value
d
A double-precision float in the native format
f
A single-precision float in the native format
H
A hex string, high nibble first
h
A hex string, low nibble first
i
A signed integer value
I
An unsigned integer value
l
A signed long value
L
An unsigned long value
N
A long in network order
n
A short in network order
p
A pointer to a null-terminated string
P
A pointer to a structure (fixed-length string)
s
A signed short value
S
An unsigned short value
u
A uuencoded string
V
A long in little-endian order
v
A short in little-endian order
x
A null byte
X
Back up a byte

Now that you have bound an address for your server or client, you can connect to it or listen for connections with it. If your program is a server, it will set itself up to listen and accept connections.

Tip
Do not use S n A4 x8 instead of S n C4 x8 for packing into the sockaddr structure. The C4 specifies four unsigned char data values and is safe to use. The A4 implies a string, which may confuse the pack() function if there are nulls (zeroes) in the address.

Now let's look at the functions available for use in a server.

The listen() and accept() System Calls

The listen() system call is used by the server to listen for connections. Once it is ready to listen, the server is able to honor any requests for connections with the accept system call. The listen call is defined as this:

listen(SOCKET_HANDLE, $queueSize);

The SOCKET_HANDLE is the descriptor of the socket you created. The queueSize is the number of waiting connections allowed at one time before any are rejected. Use the standard value of 5 for queue size. A returned value of NULL indicates an error. The call to listen normally looks like this:

listen(MY_HANDLE,5) || die "Cannot listen $!\n";

If this call is successful, you can accept connections with the accept function, which looks like this:

accept(NEWSOCKET, ORIGINAL_SOCKET);

The accept() system call is used by the server to accept any incoming messages from a client's connect() calls. Be aware that this function will not return if no connections are received. As requests come off the queue and set up in the listen() call, the accept function handles them by assigning them to a new socket. NEWSOCKET is created by the accept function as ORIGINAL_SOCKET, but now NEWSOCKET is going to be used to communicate with the client. At this point, most servers fork off (fork()) a child process to handle the client and go back to wait for more connections. Before I get into that, let's see how connections are originated.

Let's look at the connect() call that you'll use to connect to a server.

The connect() System Call

The connect() system call is used by clients to connect to a server in a connection-oriented system. This connect() call should be made after the bind() call. There are two ways you can call the connect() call: one for AF_UNIX using the pathname of the socket and the other using an address as the AF_INET requirement for a socket handle.

connect(SOCKET_HANDLE,"pathname" ); # for AF_UNIX
connect(SOCKET_HANDLE,$Address); # for AF_INET

Connection-Oriented Servers in Perl

Given this background information about socket information gathering, creation, and so on, you are now ready to write your own server using Perl. Listing 12.4 presents a sample server.


Listing 12.4. Server side for connection-oriented protocol.
 1 #!/usr/bin/perl
 2 #
 3 # Sample connection oriented server using Perl
 4 #
 5 $AF_UNIX = 1;
 6 $AF_INET=2;   # Use AF_INET instead of AF_UNIX.
 7 $SOCK_STR = 1; # Use STREAMS.
 8 $PROTOCOL = 0; # stick to the default protocols (IP).
 9
10 $port = 6668 unless $port;
11
12 #
13 # The pattern for packing into a sockaddr structure
14 #
15 $PACKIT='S n C4 x8';
16
17 #
18 # Disable any buffering on any newly created sockets.
19 #
20 select(NEWSOCKET);
21 $| = 1;
22 select(stdout);
23
24 #
25 # Create the socket.
26 #
27 socket(MY_SOCKET, $AF_INET, $SOCK_STR, $PROTOCOL) ||
28            die "\n $0: Cannot open socket: $!";
29 print "Socket successfully opened\n";
30
31 #
32 # Get the host address for this node
33 #
34
35 ($name, $aliases, $addrtype, $len, @addrs) = gethostbyname("www.ikra.com");
36 ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
37 print "Server Name=$name, Server Address= $a.$b.$c.$d\n";
38 $my_ip_addr = pack($PACKIT,$AF_INET,$port,$addrs[0]);
39
40 #
41 # If you just want to test with the localhost, try this line
42 # instead of the above.
43 # $my_ip_addr = pack($PACKIT,$AF_INET,$port,127,0,0,1);
44
45 #
46 # Bind to the socket and listen on this port
47 #
48 bind(MY_SOCKET, $my_ip_addr) || die "$0: Cannot bind .. $!\n";
49 print "\n Bound to socket";
50 listen(MY_SOCKET,5) || die "$0: Cannot listen: $!\n";
51 print "\n Listening \n";
52
53 while(1) {
54   $remote = accept(NEWSOCKET, MY_SOCKET) || die "$0: Unacceptable: $!\n";
55
56 #
57 # In case you have to display incoming connection
58 # information, you can uncomment the next three lines of code:
59
60 #     @remoteInfo = unpack($PACKIT,$remote);
61 #     $, = ' ';
62 #     print @remoteInfo; print "\n";
63
64   # $pid = fork || &cleanup;
65
66      if ($pid == fork) { # child
67      sleep 3;
68      print NEWSOCKET "Welcome to this server\n";
69      # in child,.. you can do other stuff here.
70      close NEWSOCKET;
71      # I chose to just print this message and terminate
72      close MY_SOCKET;
73      exit;
74   }
75   else { # parent
76      sleep 10;
77      close MY_SOCKET;
78      close NEWSOCKET; # in parent
79      exit;
80   }
81
82 }
83 sub cleanup { close NEWSOCKET; close(MY_SOCKET); die "$0: Cannot fork : $!\n"; }

Note
Of course, instead of the IP addresses shown above in Listing 12.4, you would have to modify the Perl script to use your own machine name and IP address. Do not use the addresses shown in this example because they are coded to work with a specific machine with a specific name.

In the case of connection-oriented protocols, the server does the following functions:

  • Creates a socket with a call to the socket() function.
  • Binds itself to an address with the bind() function call.
  • Listens for connections with the listen() function call.
  • Accepts any incoming requests with the accept() function call.

Once a connection to the server has been accepted, the client and server can exchange data with the read() and write() function calls. To read from the socket, use the function call

read(MY_SOCKET, $buffer, $length);

where SOCKET_HANDLE is the socket you are reading from and $buffer is where you will be putting in data of size $length. To write to the socket, you can use the function call

write(MY_SOCKET, $buffer, $length);

For sending just text data, you can use the print call instead. For example, the following code will write text to the socket:

print MY_SOCKET, "Hello, ..";

Once a connection has served its time, it has to be closed so that other clients are able to use the system resources. To close the socket, your server and clients should call the close() function:

close(MY_SOCKET);

The shutdown() function allows you to selectively shut down sends and receives on a socket. Here's the function call:

shutdown(MY_SOCKET,HOW);

When the HOW parameter is 0, no more data is received on this socket. If HOW is set to 1, no more data will be sent out on this socket. If set to 2, no data is received or sent on this socket. (You still have to close the socket, even if you shut it down for sending and receiving.)

Listing 12.5 presents a sample of the client side of things.


Listing 12.5. The client side.
 1 #!/usr/bin/perl
 2 #
 3 # define constants for talking to server
 4 #
 5 $PACKIT = 'S n C4 x8';
 6 $AF_INET = 2;
 7 $SOCK_STR = 1;   # STREAMS
 8 $DEF_PROTO = 0;  #default protocol of IP
 9 $port = 6668 unless $port;
10
11 #
12 # Get the name of the server
13 #
14 ($name, $aliases, $addrtype, $len, @addrs) = gethostbyname("www.ikra.com");
15 ($a,$b,$c,$d) = unpack('C4',$addrs[0]);
16 print "Server Name=$name, Server Address= $a.$b.$c.$d\n";
17 $that = pack($PACKIT,$AF_INET,$port,$addrs[0]);
18
19 #
20 # Confine yourself to the local host machine address of 127.0.0.1
21 #
22 $this = pack($PACKIT,$AF_INET,$port,127,0,0,1);
23
24 #
25 # Disable buffering on this socket.
26 #
27 select(CLIENT_SOCKET);
28 $| = 1;
29 select(stdout);
30
31
32 socket(CLIENT_SOCKET,$AF_INET,$SOCK_STR,$DEF_PROTO) ||
33   die "$0: Cannot open socket\n";
34 print "Created socket\n";
35
36 #
37 # Attempt to connect to server.
38 #
39 do
40 {
41 sleep(1);
42 $result = connect(CLIENT_SOCKET,$that);
43 if (result != 1) {
44   print "Sleeping\n";
45   }
46
47 } while ($result != 1);
48
49 sleep(5);
50 print "After connection\n";
51
52 #
53 # send data to server
54 #
55 # write(CLIENT_SOCKET,"hello",5);
56
57 read(CLIENT_SOCKET,$buf,100);
58
59 print "[" . $buf . "]\n";
60 close(CLIENT_SOCKET);

The client for connection-oriented communication also takes the following steps:

  • Creates a socket with a call to the socket() function.
  • Gets the packed structures for identifying itself and the server to which it will connect.
  • Attempts to connect to the server with a connect() call.
  • If a connection was made, requests for data with the write() call and reads incoming replies with the read() function.

Tip
After the connection is used up by the client, it's a good idea to close the socket so that others may use the system resources.

That's about it for a client program. Any processing that has to be done is done while the connection is open. Client programs can be written to keep a connection open for a long time while large amounts of data are transferred. If there is too long of a delay between successive messages, clients would then open a socket connection, send the message or messages, and close the connection immediately after the acknowledgment, if any, arrives. This way, all sockets are opened only on an as needed basis and do not use up socket services when both the server and client are idle.

The h2ph Script

If you read more documentation on Perl and sockets, you'll see references to the socket.ph file. If you cannot find this file anywhere on your system, it's because you have not run the h2ph file on your include directories. This h2ph program converts C header files to Perl header files. The safest way to ensure that you have all the files converted to Perl headers is to issue the following statements while logged in as root:

$ cd /usr/include
$ h2ph *
$ h2ph sys/*

You may run into some problems while running this script. For instance, it will say that it's creating a .ph file from a .h file, but after execution, the *.ph file may not exist! Check the script in the h2ph file to see where $perlincl is pointing and if you have read/write permissions there. A common repository is the /usr/local/lib/perl5 or the /usr/lib/perl5 directory. Another thing to remember is that the @Inc variable in your Perl scripts should point to the same location where the *.ph files are placed.

Using Socket.pm

The standard Perl 5 distribution comes with the Socket.pm module, which greatly speeds up Perl code development work. Look at the documentation in the /usr/lib/Perl5/Socket.pm file for more information. This module requires dynamic loading, so ensure that your system supports it.

Summary

Perl offers very powerful features for using sockets on UNIX machines. The system calls available offer enough power and flexibility to create client/server applications in Perl without having to write code in a compiled language.

The functions available in Perl include those for querying available system services and protocols, IP addresses, and other host name information. Here are a few key points to remember when working with Perl and sockets:

  • Perl uses the existing system calls for using sockets. These system calls expect structures to be packed in a sockaddr structure. This packing and unpacking is done with the pack() and unpack() functions prior to use with system calls.
  • You use read() and write() functions to do direct data transfer. You can use print SOCKET, … if you plan on writing directly to the port.
  • Disable buffering on the socket with the $|=1 assignment on a socket. This forwards any reads or writes immediately instead of being buffered internally, preventing unnecessary timeouts in case of long operations.
  • Always call the close() function of the socket when your application is done with communication.
  • Servers generally fork off copies of themselves to handle individual connections. Be prepared to write code this way.


Previous chapter Chapter contents Contents Next chapter




With any suggestions or questions please feel free to contact us