A More Fully Commented Version of server.c
/*============================================================*
File name : server.c
Related file: client.c
Author : Ting-jen Yen (yentj@cs.nyu.edu)
Date : 1998 (for Yap's Visualization Class)
Description : This is a simple iterative
server that listens at port 1234.
It will accept a number (unsigned, integer or
float) from the client, and returns twice
the number to the client. You can
can access this server by running the client
program (see file client.c), or just using
"telnet" to connect to the server.
Usage: Simply type "server" in the host machine to serve
at default port 1234. Otherwise type
"server -p ".
How to compile :
On Solaris 2 :
% gcc -o server server.c -lsocket
On SGI machies :
% gcc -o server server.c
*============================================================*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
int server_sock, con_sock;
int srv_port = 1234;
struct sockaddr_in source;
/* Close all sockets before the forced quit caused by signal
*/
void
signal_handler(int n)
/* Chee, 7/23/01: changed from "void signal_handler()".
See child_end(int) for explanation of similar change. */
{
close(con_sock);
close(server_sock);
exit(0);
}
/* When the state of a child process is changed (for example,
it is terminated, or ended by exit() call), the server
should call wait() to get the state. This will prevent
the child process from becoming a zombie process when it has
finished. */
void child_end(int n) /* Chee, 7/23/01: changed from "void child_end()".
But we do nothing with the argument n.
Note that child_end is the second argument to
sigset(int, func) (see below).
In the latest /usr/include/signal.h, the second
argument to sigset(int, func) must
by a function(int) returning void. */
{
wait(NULL);
}
/*============================================================*
init_servconnection()
performs the first 3 steps of a connection-based server:
(A) socket()
(B) bind()
(C) listen()
*============================================================*/
void
init_servconnection()
{
struct sockaddr_in server;
/* Creating socket */
server_sock = socket(PF_INET, SOCK_STREAM, 0);
// This is step (A) of a connection-based server
// in our lecture notes
// It is the first step for server or client
// PF_INET is the TCP/IP family
// SOCK_STREAM requests stream service, i.e., TCP
// (the alternative is SOCK_DGRAM = datagram = UDP)
// The last argument 0 identifies which stream service
// in the protocol family (if there are no
// alternatives, 0 goes the default).
if (server_sock < 0) {
perror("socket");
exit(1);
}
server.sin_family = AF_INET;
server.sin_port = htons(srv_port); // the server is at this port
server.sin_addr.s_addr = INADDR_ANY;
/* bind the specified port to this socket */
if (bind(server_sock, (struct sockaddr *) & server, sizeof(server))
< 0)
// BINDING is step (B) of a connection-based server
// in our lecture notes. The server uses this
// to register its well-known port. This information
// is in the second argument ("server" structure).
{
perror("bind socket");
exit(1);
}
/* listen for connection */
if (listen(server_sock, 5) < 0) {
perror("listen");
exit(1);
}
// LISTENING is step (C) of a connection-based server
// in our lecture notes. It tells the system that the
// server is READY waiting
// for clients (the second argument "5" is the maximum
// number of clients allowed in the queue.
// IMPORTANT: it does not mean that the server is already
// waiting. That is step (D), called ACCEPT().
}// init_servconnection
/*============================================================*
atteneconnection()
performs the servicing of clients: first, it
(D) accept()
and the forks. The parent process returns to
accepting, but the child calls a subroutine
"handle_req()" to actually handle requests.
*============================================================*/
attenteconnection()
{
int lg, pid;
lg = sizeof(struct sockaddr_in);
/* repeat until accept a successful connect */
do
con_sock = accept(server_sock, (struct sockaddr *) & source, &lg);
// ACCEPT is step (D) of a connection-based server
// in our lecture notes. When con_sock>0, it means
// the server actually got a connection with a client.
while (con_sock <= 0);
/* Fork a child process to serve the client */
if ((pid = fork()) != 0)
{
/* parent process can close the connect and quit */
close(con_sock);
}
else
{
/* The child process will handle the client, and the quit when
finished. */
handle_req();
exit(1);
}
}// attenteconnection
usage()
{
fprintf(stderr, "usage homesrvd [-p port]\n");
exit(1);
}
extern int errno;
/*============================================================*
handle_req()
will get input from the client, parse
it and write the result back to the client. In this
case, it will accept a number, either integer or
float-point value, and return twice the number.
It will repeat until the input is string "quit".
*============================================================*/
handle_req()
{
char buff[8192];
int lg, flag, srv1, is_float, result_i;
char *filename, *c, *p;
struct stat statres;
char req[1024];
double result_f;
flag = 1;
while (flag) {
// Try to repeat until read something from the client,
// unless the connection is broken.
do {
lg = read(con_sock, req, 1024);
// READ is step (E) of a connection-based server
// in our lecture notes.
if (lg == -1 && errno == EBADF)
goto error;
} while (lg <= 0);
// Assume the input will be integer first.
is_float = 0;
if (req[lg - 1] != '\n' || req[lg - 2] != '\r') {
sprintf(req, "Error: Unknown input\r\n");
write(con_sock, req, strlen(req));
continue;
}
// Get rid of the trailing new line character and
// carriage return character.
req[lg - 2] = '\0';
// Get the first part of the line only,
// separated by space.
c = strtok(req, " ");
// Empty line
if (c == NULL) {
sprintf(req, "Error: Unknown input\r\n");
write(con_sock, req, strlen(req));
continue;
}
// quit
if (strncmp(c, "quit", 4) == 0)
{
flag = 0;
break;
}
// Check every character to make sure the input is a
// number, determining it is an integer or not on the way.
for (p = c; *p /* != NULL */ ; p ++)
{
if (!isdigit(*p))
{
if (*p != '.' || is_float)
{
// If the character is not a digit, and not a decimal point,
// then it is not a number. If it is a decimal point, but
// there is already another one, then it is not a legal number.
*/
sprintf(req, "Error: Unknown input\r\n");
write(con_sock, req, strlen(req));
is_float = -1;
break;
}
else
/* If we have one decimal point, then the number will not
be an integer. */
is_float = 1;
}
}
if (is_float == 1)
{
/* A float-point number */
result_f = atof(c) * 2;
sprintf(req, "%f\r\n", result_f);
write(con_sock, req, strlen(req));
}
else if (is_float != -1)
{
/* An integer */
result_i = atoi(c) * 2;
sprintf(req, "%d\r\n", result_i);
write(con_sock, req, strlen(req));
}
}
/* Some connection error happened, or quit the loop
because receiving "quit" from the client. Will
close connection and exit this function. */
error:
close(con_sock);
}
main(int argc,char **argv)
{
int lg;
/* Shift the parameter */
argc--;
argv++;
while (argc > 0) {
if (argv[0][0] != '-')
usage();
switch (argv[0][1]) {
case 'p':
/* Set port number of the server */
srv_port = atoi(argv[1]);
argv += 2;
argc -= 2;
break;
default:
usage();
}
}
/* Intercept CHILD signal. This signal occurs when a child process
changes its status. In this program, it usually means when
a child process calls exit() to end itself. */
// sigset(SIGCHLD, child_end);
signal(SIGCHLD, child_end);
/*Chee, 7/23/01:
in latest /usr/include/signal.h, child_end must by
a function(int) returning void. */
/* Intercept some signals which will end this process so
we can close connection before quit it. */
signal(SIGTERM, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGINT, signal_handler);
/* Setup the socket and the port */
init_servconnection();
/* Wait for connection */
do {
attenteconnection();
} while (1);
}