DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
Programming with sockets

Servers

In the UNIX system, most servers are accessed at well known Internet addresses or UNIX domain names. The form of their main loop is illustrated by the following code from a remote-login server:

   main(argc, argv)
   	int argc;
   	char *argv[];
   {
   	int f;
   	struct sockaddr_in from;
   	struct sockaddr_in sin;
   	struct servent *sp;
   

sp = getservbyname("login", "tcp"); if (sp == NULL) { fprintf(stderr, "rlogind: tcp/login: unknown service\n"); exit(1); } ... #ifndef DEBUG /* Disassociate server from controlling terminal. */ ... #endif sin.sin_len = sizeof(sin); sin.sin_port = sp->s_port; /* Restricted port */ sin.sin_addr.s_addr = INADDR_ANY; ... f = socket(AF_INET, SOCK_STREAM, 0); ... if (bind(f, (struct sockaddr *)&sin, sizeof(sin)) < 0) { ... } ... listen(f, 5); for (;;) { int g, len = sizeof(from);

g = accept(f, (struct sockaddr *) &from, &len); if (g < 0) { if (errno != EINTR) syslog(LOG_ERR, "rlogind: accept: %m"); continue; } if (fork() == 0) { close(f); doit(g, &from); } close(g); } exit(0); }

The first step taken by the server is look up its service definition:
   sp = getservbyname("login", "tcp");
   if (sp == NULL) {
   	fprintf(stderr,
   		"rlogind: tcp/login: unknown service\n");
   	exit(1);
   }
The result of the getservbyname call is used in later portions of the code to define the Internet port at which it listens for service requests (indicated by a connection). Some standard port numbers are given in the file /usr/include/netinet/in.h for backward compatibility purposes.

Step two is to disassociate the server from the controlling terminal of its invoker:

   for (i = getdtablesize()-1; i >= 0; --i)
   	close(i);
   

open("/dev/null", O_RDONLY); dup2(0, 1); dup2(0, 2);

i = open("/dev/tty", O_RDWR); if (i >= 0) { ioctl(i, TIOCNOTTY, 0); close(i); }

This step is important as the server will probably not want to receive signals delivered to the process group of the controlling terminal. Note, however, that once a server has disassociated itself it can no longer send reports of errors to a terminal, and must log errors via syslog.

Once a server has established a pristine environment, it creates a socket and begins accepting service requests. The bind call is required to insure the server listens at its expected location. Note that the remote login server listens at a restricted port number, and must therefore be run with a user-id of root. This concept of a ``restricted port number'' is covered in the ``Advanced topics''.

The main body of the loop is simple:

   for (;;) {
   	int g, len = sizeof(from);
   

g = accept(f, (struct sockaddr *)&from, &len); if (g < 0) { if (errno != EINTR) syslog(LOG_ERR, "rlogind: accept: %m"); continue; } if (fork() == 0) { /* Child */ close(f); doit(g, &from); } close(g); /* Parent */ }

An accept call blocks the server until a client requests service. This call could return a failure status if the call is interrupted by a signal such as SIGCHLD (to be discussed in ``Advanced topics''). Therefore, the return value from accept is checked to insure a connection has been established, and an error report is logged via syslog if an error has occurred.

With a connection in hand, the server then forks a child process and invokes the main body of the remote login protocol processing. Note how the socket used by the parent for queuing connection requests is closed in the child, while the socket created as a result of the accept is closed in the parent. The address of the client is also handed to the doit routine because it requires it in authenticating clients.


© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005