Topic : Socket Programming
Author : Reg Quinton
Page : << Previous 4  Next >>
Go to page :


run the whois daemon (on the server) to see what it does:

[3:27pm julian] echo reggers | /usr/lib/whois/whoisd
There were 1 matches on your request.

           Full Name: Quinton, Reg
          Department: Info Tech Svcs
                Room: NSC 214
               Phone: 679-2111x(6026)
           Index Key: 481800
     Machine Address: reggers@julian.uwo.ca
Directory Addresses: reg.quinton@uwo.ca
                    : r.quinton@uwo.ca
                    : reggers@uwo.ca
                    : quinton@uwo.ca

For more information try 'whois help'.

The program is command driven -- you give a command (or query string) on stdin, it produces results on stdout, and exits. This is a very simple protocol, compare with fingerd(1M).

Actually the example is a misrepresentation -- our server will only answer questions if it's input is a socket in the AF_INET. That's because we want to syslog(3) all transactions -- we want to know where the connection came from.

The Code:
The server program is easy enough -- read a line, switch on command, and exit.

fgets(string,BUFSIZ,stdin); read from socket...

/* for some reason people send the whois phrase */

again:
     strcpy(verb,""); strcpy(args,"");
     sscanf(buf,"%[^ \t\r\n]%*c%[^\r\n]",verb,args);
     if (!strcasecmp(verb,"whois")) {
       strcpy(buf,args);
       goto again;
     }
     sscanf(buf,"%[^\r\n]",buf);

/* switch on command verbs */
if (!strcasecmp(verb,"help"))
       givehelp(args);        output sent to stdout...
else   etc...

/* or just display a person */
else    listdisplay(lookbyname(buf));
                              output sent to stdout...

fflush(stdout);       push output to client ...


Server programs can be that simple.

Connecting to the Server:
You can make a telnet(1) connection to the whois service on the server.

[3:47pm julian] telnet julian whois
Trying 129.100.2.12 ... Connected to julian.uwo.ca.
Escape character is '^]'.
reggers .... my command input
There were 1 matches on your request.

           Full Name: Quinton, Reg
          Department: Info Tech Svcs
                Room: NSC 214
               Phone: 679-2111x(6026)
           Index Key: 481800
     Machine Address: reggers@julian.uwo.ca
Directory Addresses: reg.quinton@uwo.ca
                    : r.quinton@uwo.ca
                    : reggers@uwo.ca
                    : quinton@uwo.ca

For more information try 'whois help'.
Connection closed by foreign host.

But we wouldn't normally use telnet as the client application (although in this case we could).


Whois Client:
The whois(1) client makes a TCP/IP connection to the server (using the tcpopen function we've developed here) and conducts the kind of protocol that you would type if you where to make a connection by hand:

[7:30am julian] whois reggers
There were 1 matches on your request.

           Full Name: Quinton, Reg
          Department: Info Tech Svcs
                Room: NSC 214
               Phone: 679-2111x(6026)
           Index Key: 481800
     Machine Address: reggers@julian.uwo.ca
Directory Addresses: reg.quinton@uwo.ca
                    : r.quinton@uwo.ca
                    : reggers@uwo.ca
                    : quinton@uwo.ca

For more information try 'whois help'.

The client sends the command "reggers", the server sends back the answer and the client displays the answer received to the user. When the server is finished the connection is closed.

If you understand the development of the tcpopen function then the rest of the code for that client should not be too difficult. See the entire distribution for that application -- there's only one main program to complete the kit.


Perl Socket Programming:
These days it's not unusal to see socket programming in perl(1) as well as C programs. Assuming you have been able to follow the notions presented above in the development of a tcpopen function written in C as used by our whois(1) client the following is for the Perl enthusiast:

sub tcpopen {
   use Socket;                        # need socket interface
   my($server, $service) = @_;        # args to this function
   my($proto, $port, $iaddr);         # local variables
   my($handle)="$server\:\:$service"; # localized obscure handle

   die("550:Cannot getprotobyname('tcp')\r\n")
      unless ($proto = getprotobyname('tcp'));

   die("550:Cannot getservbyname($service)\r\n")
      unless ($port = getservbyname($service, 'tcp'));

   die("550:Cannot gethostbyname($server)\r\n")
      unless ($iaddr = gethostbyname($server));

   die("550:Cannot create socket\r\n")
      unless socket($handle, PF_INET, SOCK_STREAM, $proto);

   die("550:Cannot connect($service://$server)\r\n")
      unless connect($handle, sockaddr_in($port, $iaddr));

   # unbuffered I/O to that service

   select($handle); $| = 1; select(STDOUT); $| = 1;

   return($handle);
}


See whois2ph(8), the whois2ph source, whois2html(8), and the whois2html source -- both are production gateways in Perl to interface with our whoisd(8) server.




Final Comments:
The whois example uses a line based protocol. The strategy is common but by no means universal. For example, the lpd protocols use octets (ie. single characters) for the commands.

Inetd servers are the simplest to implement. However, this may not be optimal. Especially if the server has to do a lot of work first (eg. loading in a big data base).

Stand alone servers have to deal with many daemon issues -they should ignore most signals, set a unique process group and get rid of the controlling terminal.

Daemons like nntp could (in theory) handle many clients from a single daemon using interrupt driven I/O. As currently implemented most have an nntp daemon for each client (but INN uses a single daemon for flooding).

You'll note that Socket programmers use alarm(2), setjmp(2), and signal(2) calls. The intent is to prevent a process (client or server) from hanging in a wait for I/O state by setting and trapping on an alarm.

Note Well:
The best way to code a client/server program is to reuse code from an existing service. There's lots of public domain examples to work from -- nntp, lpd, sendmail, and even our whois service.
A simple solution that works is much better than a fancy solution that doesn't -- keep it simple.
Presentation issues, ie. the display for the user, should not effect the protocol or server. Again, protocols have to be simple!
Don't ever assume the client or server applications are well behaved!




Suggested Reading:
It shoud be clear that we have lots of real world examples you can look at and work from:
UWO/ITS Whois/CSO server, by Reg Quinton, UWO/ITS, 1992-97
UWO/ITS Whois client, by Reg Quinton, UWO/ITS, 1992-97
Passwdd/Passwd -- An authentication Daemon/Client, by Reg Quinton, UWO/ITS, 1992-97
ACL -- Access Control Lists, by Reg Quinton, UWO/ITS, 1995-97
More detailed documentation, should you need it, can be found at:
Introductory 4.3BSD Interprocess Communication, by Stuart Sechrest, (in) UNIX Programmer's Supplementary Documents, Vol1, 4.3 Berkeley Software Distribution, PS1:7.
Advanced 4.3BSD Interprocess Communication, by Samuel J. Leffler et al, (in) UNIX Programmer's Supplementary Documents, Vol1, 4.3 Berkeley Software Distribution, PS1:8.
Introduction to the Internet Protocols, Computer Science Facilities Group, Rutgers. (See ftp:/ftp.uwo.ca/nic)
Networking with BSD-style Sockets, by John Romkey,

Page : << Previous 4  Next >>