Sockets and Servers
Most servers are accessed at well-known Internet port numbers or UNIX
family names. The service rlogin
is an example of a
well-known UNIX family name. The main loop of a remote login server is
shown in Example 7-7.
The server dissociates from the controlling terminal of its invoker
unless the server is operating in DEBUG
mode.
(void) close(0); (void) close(1); (void) close(2); (void) open("/", O_RDONLY); (void) dup2(0, 1); (void) dup2(0, 2); setsid();
Dissociating prevents the server from receiving signals from the process group of the
controlling terminal. After a server has dissociated from the controlling terminal,
the server cannot send reports of errors to the terminal. The dissociated server
must log errors with syslog
. For more information, see the
syslog
(3C) man page.
The server gets its service definition by calling getaddrinfo
. For more
information, see the
getaddrinfo
(3C) man page.
bzero(&hints, sizeof (hints)); hints.ai_flags = AI_ALL|AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(NULL, "rlogin", &hints, &api);
The result, which is returned in api
,
contains the Internet port at which the program listens for service
requests. Some standard port numbers are defined in
/usr/include/netinet/in.h
.
The server then creates a socket, and listens for service requests. The
bind
()
routine ensures that the server listens at the
expected location. Because the remote login server listens at a
restricted port number, the server runs as superuser. The main body of
the server is the following loop.
Example 7-7 Showing a Server Main Loop
/* Wait for a connection request. */ for (;;) { faddrlen = sizeof (faddr); new_sock = accept(sock, (struct sockaddr *)api->ai_addr, api->ai_addrlen) if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("rlogind: accept"); } continue; } if (fork() == 0) { close (sock); doit (new_sock, &faddr); } close (new_sock); } /*NOTREACHED*/
accept
()
blocks messages until a client requests
service. accept
()
also returns a failure indication
if accept
()
is interrupted by a signal, such as
SIGCHLD
. The return value from
accept
()
is checked, and an error is logged with
syslog
, if an error occurs. For more information, see
the
accept
(3C) and
syslog
(3C) man pages.
The server then forks a child process, and invokes the main body of the remote login protocol
processing. The socket used by the parent to queue connection requests is closed in
the child. The socket created by accept
is closed in the parent.
The address of the client is passed to the server application's
doit
()
routine, which authenticates the client.