HPlogo HP 9000 Networking: BSD Sockets Interface Programmer's Guide > Chapter 4 Using Internet Datagram Sockets

Example Using Datagram Sockets

» 

Technical documentation

Complete book in PDF

 » Table of Contents

 » Glossary

 » Index

NOTE: These programs are provided as examples only of datagram socket usage and are not Hewlett-Packard supported products.

These program examples demonstrate how to set up and use datagram sockets. These sample programs can be found in the
/usr/lib/demos/networking/socket directory. The client program is intended to run in conjunction with the server program.

This example implements a simple name server. The server process receives requests from the client process. It determines the internet address of the specified host and sends that address to the client process. If the specified host's internet address is unknown, the server process returns an address of all 1s.

The client process requests the internet address of a host and receives the results from the server process.

Before you run the example programs, make the following entry in the two hosts /etc/services files:

example  22375/udp

The source code for these two programs follows.

/*                       S E R V . U D P
*
* This is an example program that demonstrates the use of
* datagram sockets as an BSD Sockets mechanism. This contains
* the server, and is intended to operate in conjunction with the
* client program found in client.udp. Together, these two
* programs demonstrate many of the features of sockets, as well
* a good conventions for using these features. NOTE: This example
* is valid only if the /etc/hosts file is being used to lookup
* host names.
*
* This program provides a service called "example". It is an
* example of a simple name server. In order for
* it to function, an entry for it needs to exist in the
* /etc/services file. The port address for this service can be
* any port number that is likely to be unused, such as 22375,
* for example. The host on which the client will be running
* must also have the same entry (same port number) in its
* /etc/services file.
*
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>

int s; /* socket descriptor */

#define BUFFERSIZE 1024 /* max size of packets to be received */
int cc; /* contains the number of bytes read */
char buffer[BUFFERSIZE]; /* buffer for packets to be read into */

struct hostent *hp; /* pointer to info of requested host */
struct servent *sp; /* pointer to service information */

struct sockaddr_in myaddr_in; /* for local socket address */
struct sockaddr_in clientaddr_in;/* for client's socket address */
struct in_addr reqaddr; /* for requested host's address */

#define ADDRNOTFOUND 0xffffffff /* return address for unfound host */

/*
* M A I N
*
* This routine starts the server. It forks, leaving the child
* to do all the work, so it does not have to be run in the
* background. It sets up the socket, and for each incoming
* request, it returns an answer. Each request consists of a
* host name for which the requester desires to know the
* internet address. The server will look up the name in its
* /etc/hosts file, and return the internet address to the
* client. An internet address value of all ones will be returned
* if the host name is not found. NOTE: This example is valid
* only if the /etc/hosts file is being used to lookup host names.
*
*/
main(argc, argv)
int argc;
char *argv[];
{
int addrlen;

/* clear out address structures */
memset ((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
memset ((char *)&clientaddr_in, 0, sizeof(struct sockaddr_in));

/* Set up address structure for the socket. */
myaddr_in.sin_family = AF_INET;
/* The server should receive on the wildcard address,
* rather than its own internet address. This is
* generally good practice for servers, because on
* systems which are connected to more than one
* network at once will be able to have one server
* listening on all networks at once. Even when the
* host is connected to only one network, this is good
* practice, because it makes the server program more
* portable.
*/
myaddr_in.sin_addr.s_addr = INADDR_ANY;
/* Find the information for the "example" server
* in order to get the needed port number.
*/
sp = getservbyname ("example", "udp");
if (sp == NULL) {
printf("%s: host not found",
argv[0]);
exit(1);
}
myaddr_in.sin_port = sp->s_port;

/* Create the socket. */
s = socket (AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
perror(argv[0]);
printf("%s: unable to create socket\n", argv[0]);
exit(1);
} /* Bind the server's address to the socket. */
if (bind(s, &myaddr_in, sizeof(struct sockaddr_in)) == -1) {
perror(argv[0]);
printf("%s: unable to bind address\n", argv[0]);
exit(1);
}

/* Now, all the initialization of the server is
* complete, and any user errors will have already
* been detected. Now we can fork the daemon and
* return to the user. We need to do a setpgrp
* so that the daemon will no longer be associated
* with the user's control terminal. This is done
* before the fork, so that the child will not be
* a process group leader. Otherwise, if the child
* were to open a terminal, it would become associated
* with that terminal as its control terminal. It is
* always best for the parent to do the setpgrp.
*/
setpgrp();

switch (fork()) {
case -1: /* Unable to fork, for some reason. */
perror(argv[0]);
printf("%s: unable to fork daemon\n", argv[0]);
exit(1);

case 0: /* The child process (daemon) comes here. */
/* Close stdin, stdout, and stderr so they will
* not be kept open. From now on, the daemon will
* not report any error messages. This daemon
* will loop forever, waiting for requests and
* responding to them.
*/
fclose(stdin);
fclose(stdout);
fclose(stderr);
/* This will open the /etc/hosts file and keep
* it open. This will make accesses to it faster.
* If the host has been configured to use the NIS
* server or name server (BIND), it is desirable
* not to call sethostent(1), because a STREAM
* socket is used instead of datagrams for each
* call to gethostbyname().
*/
sethostent(1);
for(;;) {
/* Note that addrlen passed as a pointer
* so that the recvfrom call can return
* the size of the returned address.
*/
addrlen = sizeof(struct sockaddr_in);

/* This call will block until a new
* request arrives. Then, it will
* return the address of the client,
* and a buffer containing its request.
* BUFFERSIZE - 1 bytes are read so that
* room is left at the end of the buffer
* for a null character.
*/
cc = recvfrom(s, buffer, BUFFERSIZE - 1, 0,
&clientaddr_in, &addrlen);
if ( cc == -1) exit(1);
/* Make sure the message received is
* null terminated.
*/
buffer[cc]='\0';
/* Treat the message as a string
* containing a hostname. Search
* fot the name in /etc/hosts.
*/
hp = gethostbyname (buffer);
if (hp == NULL) {
/* Name was not found. Return
* a special value signifying
* the error.
*/
reqaddr.s_addr = ADDRNOTFOUND;
} else {
/* Copy address of host
* into the return buffer.
*/
reqaddr.s_addr =
((struct in_addr *)(hp->h_addr))->s_addr;
}
/* Send the response back to the
* requesting client. The address is
* sent in network byte order. All
* errors are ignored. The client
* will retry if it does not receive
* the response.
*/
sendto (s, &reqaddr, sizeof(struct in_addr),
0, &clientaddr_in, addrlen);
}

default: /* Parent process comes here. */
exit(0);
}
}

/*
* C L I E N T . U D P
*
* This is an example program that demonstrates the use of
* datagram sockets as an BSD Sockets mechanism. This contains
* the client, and is intended to operate in conjunction with the
* server program found in serv.udp. Together, these two programs
* demonstrate many of the features of sockets, as well as good
* conventions for using these features.
*
* This program requests a service called "example". In order for
* it to function, an entry for it needs to exist in the
* /etc/services file. The port address for this service can be
* any port number that is likely to be unused, such as 22375,
* for example. The host on which the server will be runnin
* must also have the same entry (same port number) in its
* /etc/services file.
*
* The "example" service is an example of a simple name server
* application. The host that is to provide this service is
* required to be in the /etc/hosts file. Also, the host providing
* this service presumably knows the internet addresses of many
* hosts which the local host does not. Therefore, this program
* will request the internet address of a target host by name from
* the serving host. The serving host will return the requested
* internet address as a response, and will return an address of
* all ones if it does not recognize the host name.
*
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <signal.h>
#include <netdb.h>

extern int errno;

int s; /* socket descriptor */

struct hostent *hp; /* pointer to info for nameserver host */
struct servent *sp; /* pointer to service information */

struct sockaddr_in myaddr_in; /* for local socket address */
struct sockaddr_in servaddr_in;/* for server socket addres */
struct in_addr reqaddr; /* for returned internet address */
#define ADDRNOTFOUND 0xffffffff /* value returned for unknown host */
#define RETRIES 5 /* # of times to retry before giving up */

/*
* H A N D L E R
*
* This routine is the signal handler for the alarm signal.
* It simply re-installs itself as the handler and returns.
*/
handler()
{
signal(SIGALRM, handler);
}

/*
* M A I N
*
* This routine is the client which requests service from
* the remote "example server". It will send a message to the
* remote nameserver requesting the internet address corresponding
* to a given hostname. The server will look up the name, and
* return its internet address. The returned address will be
* written to stdout.
*
* The name of the system to which the requests will be sent is
* given as the first parameter to the command. The second
* parameter should be the name of the target host for which the
* internet address is sought.
*/
main(argc, argv)
int argc;
char *argv[];
{
int i;
int retry = RETRIES; /* holds the retry count */
char *inet_ntoa();

if (argc != 3) {
fprintf(stderr, "Usage: %s <nameserver> <target>\n",
argv[0]);
exit(1);
}

/* clear out address structures */
memset ((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
memset ((char *)&servaddr_in, 0, sizeof(struct sockaddr_in));
/* Set up the server address. */
servaddr_in.sin_family = AF_INET;
/* Get the host info for the server's hostname that the
* user passed in.
*/
hp = gethostbyname (argv[1]);
if (hp == NULL) {
fprintf(stderr, "%s: %s not found in /etc/hosts\n",
argv[0], argv[1]);
exit(1);
}
servaddr_in.sin_addr.s_addr = ((struct in_addr *)
(hp->h_addr))->s_addr;
/* Find the information for the "example" server
* in order to get the needed port number.
*/
sp = getservbyname ("example", "udp");
if (sp == NULL) {
fprintf(stderr, "%s: example not found in /etc/services\n",
argv[0]);
exit(1);
}
servaddr_in.sin_port = sp->s_port;

/* Create the socket. */
s = socket (AF_INET, SOCK_DGRAM, 0);
if (s == -1) {
perror(argv[0]);
fprintf(stderr, "%s: unable to create socket\n", argv[0]);
exit(1);
}
/* Bind socket to some local address so that the
* server can send the reply back. A port number
* of zero will be used so that the system will
* assign any available port number. An address
* of INADDR_ANY will be used so we do not have to
* look up the internet address of the local host.
*/
myaddr_in.sin_family = AF_INET;
myaddr_in.sin_port = 0;
myaddr_in.sin_addr.s_addr = INADDR_ANY;
if (bind(s, &myaddr_in, sizeof(struct sockaddr_in)) == -1) {
perror(argv[0]);
fprintf(stderr, "%s: unable to bind socket\n", argv[0]);
exit(1);
}
/* Set up alarm signal handler. */
signal(SIGALRM, handler);
/* Send the request to the nameserver. */
again: if (sendto (s, argv[2], strlen(argv[2]), 0, &servaddr_in,
sizeof(struct sockaddr_in)) == -1) {
perror(argv[0]);
fprintf(stderr, "%s: unable to send request\n", argv[0]);
exit(1);
}
/* Set up a timeout so I don't hang in case the packet
* gets lost. After all, UDP does not guarantee
* delivery.
*/
alarm(5);
/* Wait for the reply to come in. We assume that
* no messages will come from any other source,
* so that we do not need to do a recvfrom nor
* check the responder's address.
*/
if (recv (s, &reqaddr, sizeof(struct in_addr), 0) == -1) {
if (errno == EINTR) {
/* Alarm went off & aborted the receive.
* Need to retry the request if we have
* not already exceeded the retry limit.
*/
if (—retry) {
goto again;
} else {
printf("Unable to get response from");
printf(" %s after %d attempts.\n",
argv[1], RETRIES);
exit(1);
}
} else {
perror(argv[0]);
fprintf(stderr, "%s: unable to receive response\n",
argv[0]);
exit(1);
}
}
alarm(0);
/* Print out response. */
if (reqaddr.s_addr == ADDRNOTFOUND) {
printf("Host %s unknown by nameserver %s.\n", argv[2],
argv[1]);
exit(1);
} else {
printf("Address for %s is %s.\n", argv[2],
inet_ntoa(reqaddr));
}
}
© 1997 Hewlett-Packard Development Company, L.P.