To push a number onto the stack, you will enter a line as follows: #:970976453 After you press Enter, the server will respond with something like this: 0: This tells you that the number
Trang 1413: rpn_process(FILE *tx,char *buf) {
Trang 2While Listing 10.5 is quite long, only the server concepts within
function will be described:
1 The rpn_process() function is called with the output stream
to write to (argument tx ), and the input text line in buf to process (line 413).
2 The variables operation and operand are the parsed operation and operand strings, respectively (lines 419 to 420).
3 If the operation is special operation "dump", the function
rpn_dump() is called to list the contents of the stack (lines
422 to 423).
4 If step 3 does not apply, and if the operation is "=", the value is popped off the stack and returned to the client program (lines 425 to 436) Proceed to step 7
5 If steps 3 and 4 do not apply, and if the operation is "#",
the operand value is pushed onto the stack (lines 438 to 448) Proceed to step 7.
back to the client is the stack index value or an error
indication (lines 454 to 460).
fflush(tx) in line 462.
How the server works from the client side, will be examined
shows the remainder of the server source code This represents the main program segment of the server.
Listing 10.6 rpnsrv.c— The RPN Main Server Code
Trang 331: * This function reports the error and
32: * exits back to the shell:
49: struct sockaddr_in adr_srvr;/* AF_INET */
50: struct sockaddr_in adr_clnt;/* AF_INET */
51: int len_inet; /* length */
52: int s = -1; /* Socket */
53: int c = -1; /* Client socket */
54: FILE *rx = NULL; /* Read stream */
55: FILE *tx = NULL; /* Write stream */
56: char buf[4096]; /* I/O Buffer */
57:
58: /*
59: * Use a server address from the command
60: * line, otherwise default to 127.0.0.1:
Trang 5relatively familiar to you now The basic steps used in this
module were as follows:
not already defined This makes the source code clearer
2 The server's address is taken from the command line, if it
is present (lines 62 and 63).
3 The mkaddr() subroutine is called to construct a server
address for us in lines 65 to 74.
4 A server socket is created (lines 79 to 81).
5 The server address is bound to the socket (lines 86 to 89).
6 The socket is made into a listening socket (lines 94 to 96).
7 The program waits for a client to connect (lines 105 to 110).
8 Input and output file streams are created in lines 115 to
126 Notice that error recovery must ensure that the
currently open streams and client socket are closed The error itself is not reported by this server, if it should occur.
Trang 69 The I/O streams are set to line buffered mode (lines 132 to 133).
139).
11.A full shutdown is performed for this client (lines 144 to 146).
12.Repeat step 7 to accommodate the next client connection.
server loop that keeps accepting client connections in single-file fashion Later, you'll learn how to write a higher-performance server that can concurrently process several clients at one time.
Trying Out the RPN Server
To compile all the related source modules for the RPN server,
$ make rpnsrv
gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpnsrv.c
gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpneng.c
gcc -c -D_GNU_SOURCE -Wall -Wreturn-type mkaddr.c
gcc rpnsrv.o rpneng.o mkaddr.o -o rpnsrv -lgmp
$
can start the server as follows:
$ /rpnsrv &
[1] 13321
$
In the output shown, the server was started with a process ID of
13321, and run in the background.
To keep things simple at this point, you'll just use the telnet
command to try out the server The next chapter will fully
outline this server's functions For now, just try some simple tests.
Caution
The server presented is not a production-grade server Some forms of incorrect input can provoke the server to abort.
Trang 7The RPN calculator computes based upon numbers that are
pushed onto the stack To perform the add operation, for
example, requires at least two numbers to exist on the stack To push a number onto the stack, you will enter a line as follows:
#:970976453
After you press Enter, the server will respond with something like this:
0:
This tells you that the number has been stacked at the bottom
of the stack (entry number zero) To stack another number, simply do the same, as follows:
#:2636364
The server will respond with
1:
of the stack at position 0. You can list the current contents of the stack by entering the following:
Trang 8To perform a binary operation, you simply enter the name of the operation or its symbol For example, to add these numbers,
operator here pops the result off the stack and displays it for you.
the prompt:
telnet>
command.
Take a few minutes now to have some fun with the new RPN calculating server program Restart the server, and see whether you can figure out how to compute the equation (3 + 2) * (2 + 4) using the calculating server just presented.
After your experiment, you deserve a break The server will be more fully explored in the next chapter as you look at more
advanced server issues For now, just take stock of the concepts you have mastered in this chapter
Trang 9What's Next
streams with your sockets You can readily appreciate how
streams will make certain tasks much simpler for your code, such as the input and output of text lines.
You also learned to be aware of the EINTR problem if the glibc
library should change or if your code is ported to another UNIX platform.
The next chapter will teach you how servers can service multiple client connections at the same time This is not as trivial as you might imagine.
Chapter 11 Concurrent Client Servers
All of the servers presented in this text so far have processed one client's request in total before accepting a connection to the next client This design is effective and simple for servers that reply swiftly However, if the processing takes a long time,
or there are periods of inactivity, then this will prevent other clients from being serviced without lengthy delays Because servers are usually required to service as many clients as
possible, with a minimum of delay, a fundamental design
change is needed at the server end of the connection
In this chapter you will learn how to use the following:
• The fork(2) function in order to handle multiple client
connections
• The wait(2) and waitpid(2) functions
• The select(2) function for handling multiple client
connections
Mastering these concepts will permit you to write grade servers, which can service large numbers of clients at once.
Trang 10Understanding the Multiple-Client
Problem
Figure 11.1 shows several clients, which have contacted one server The client connections conceptually form spokes around the central server
Figure 11.1 Several clients attached to one server can be graphically represented as spokes attached to a hub.
balance its resources among several connected clients The server is normally designed to behave such that each client thinks that it has dedicated server access In reality, however, the server services all clients in a concurrent manner.
There are a few of ways of achieving this They are
• One process and a select(2) call
• One process and a poll(2) call
simplest way to service multiple-client processes However, it suffers from the dis-advantage that sharing information
becomes more complex This usually requires the use of
Trang 11message queues, shared memory, and semaphores It also
suffers from the disadvantage that it requires more CPU to start and manage a new process for each request.
The threaded server method is relatively new to UNIX, and is now a viable option for Linux Kernel versions 2.0.0 and later support threads, provided that the appropriate thread-safe
libraries are used Threads offer the lightweight advantages of the multi-process method, without hampering centralized
communication Threaded processes can be very difficult to debug, however, especially for beginning programmers For this reason, threads will not be explored in this text.
Tip
Frequently Asked Question (FAQ) documents that describe
threads in more detail under Linux are available on the Internet for your viewing Some references to these are
http://metalab.unc.edu/pub/Linux/docs/faqs/Threads-FAQ/html http://linas.org/linux/threads-faq.html
http://pauillac.inria.fr/~xleroy/linuxthreads
http://www.globenet.it/~ermmau/Threads
poll(2) function calls Each of these functions offer a different way to block execution of the server until an event occurs The
select(2) function will be examined in detail within this chapter
poll(2) after completing this chapter
Overview of Server Functions
Chapter 10, "Using Standard I/0 on Sockets," introduced the Reverse Polish Notation (RPN) calculating server Only its most primitive functions were described, however Before you dive into the server's design aspects in this chapter, you should get
to know some of the server's capabilities first In this manner, you'll be able to give the server a better workout
Trang 12The most basic functions are listed in Table 11.1 These describe the most rudimentary arithmetic and operating functions
available.
Table 11.1 The Rudimentary RPN Server Functions
stack The numeric resulting number replaces these two values on the top of the stack.
last number on the stack The result replaces the two values on the top of the stack.
stack The resulting value replaces the two numbers on the top of the stack.
number on the top of the stack The integer result replaces these two numbers
on the top of the stack.
with the modulo result (remainder) of the top number divided into the next to last number.
and returned to the client.
client The stack is left unmodified.
Listing 11.1 shows a simple calculation being performed Then the functions dump and = are tested prior to exiting the server
Listing 11.1 Testing the Basic RPN Server Functions
Trang 131 The three values 3, 4, and 7 are first pushed onto the stack.
place of the inputs.
see that the values 3 and 11 remain on the stack.
5 The multiplication of 3 * 11 is evaluated when * is entered
Trang 149 The telnet session is closed using CTRL+] and then the c
11.2
Table 11.2 Unary RPN Server Functions
absolute value of that number.
negated value of that number.
integer square root of that number.
Note that the unary functions only require one number to exist
that the RPN server supports.
Table 11.3 Advanced RPN Server Functions
between the top two numbers on the stack The result replaces the top two numbers on the stack.
random number seed value There is no result pushed onto the stack.
random N/A Use the value on the top of the stack to act
as the largest value + 1 for the random number to be generated The random result replaces the input value on the stack.
tprime N/A Test the top stack value to see if it is a
prime number The second-to-last number
on the top of the stack indicates how many tests to apply A typical value is 25. The result replaces the two input values on the
Trang 15Table 11.3 Advanced RPN Server Functions
stack A result of 1 indicates that the
indicates that the number is not prime.
genprime N/A Generate a prime number, using the top of
the stack as a "maximum random number + 1" value (see random ) The second-to-last number on the stack indicates the number
of tests to perform (typically 25) The generated result replaces the top two values.
Useful for exchanging two numbers.
The use of the seed and random functions require a bit of
random number generator This is important if you want to
reproduce a set of random numbers for subsequent tests For example:
input value, the random number generated could be between the values of 0 and 2999.
The genprime function works similarly Take for example:
#:25
0:
Trang 16E:end of stack dump
This example shows how a large prime number is generated
tested 25 times to see if it is a prime number If you need better assurance that the number is prime, you must use a larger test
produces a test result of zero or one instead One indicates the number was tested as probably prime
Note
The prime number tests are implemented in the GMP library
"probabilistic primality test" based upon the work by Donald E
Knuth, The Art of Computer Programming, vol 2, Seminumerical
Algorithms, 2nd edition, Addison-Wesley, 1981.
The result of the function indicates a high probability that the number is prime The probability of returning a false positive indication is (1/4)<+> r , where r represents the number of tests
is a reasonable number for r.
Using fork(2) to Service Multiple Clients
rpnsrv.c module All other source modules remain the same as they appeared in the previous chapter
Listing 11.2 rpnsrv.c— The fork(2) Modified RPN Server
Trang 1748: * This function reports the error and
49: * exits back to the shell:50: */
Trang 1866: struct sockaddr_in adr_srvr;/* AF_INET */
67: struct sockaddr_in adr_clnt;/* AF_INET */
68: int len_inet; /* length */
69: int s = -1; /* Socket */
70: int c = -1; /* Client socket */
71: FILE *rx = NULL; /* Read stream */
72: FILE *tx = NULL; /* Write stream */
73: char buf[4096]; /* I/O Buffer */
74: pid_t PID; /* Process ID */
82: * Use a server address from the command
83: * line, otherwise default to 127.0.0.1:
Trang 19136: * Fork a new server process
137: * to service this client: >
Trang 20gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpnsrv.c
gcc -c -D_GNU_SOURCE -Wall -Wreturn-type rpneng.c
gcc -c -D_GNU_SOURCE -Wall -Wreturn-type mkaddr.c
gcc rpnsrv.o rpneng.o mkaddr.o -o rpnsrv -lgmp
$ /rpnsrv '*:9090' &
[2] 915
$
you are not running the X Window system, you can use various virtual console sessions to accomplish the same effect.
The principle changes to the module are as follows:
• The <sys/wait.h> and <signal.h> include files were added in lines 16 and 17.
• The SIGCHLD signal handler was installed at line 79.
• A call to fork(2) was added in lines 139 to 147.
Trang 21• A call to exit(2) was added at line 189
Understanding the Overall Server Process
The basic main program of the server now functions as follows:
1 A signal handler for SIGCHLD is installed at line 79 This will play a role for terminated processes, which will be
discussed later.
2 A server address and socket are created (lines 85 to 112).
3 The socket is converted to a listening socket (lines 117 to 119).
4 The main loop begins (line 124).
5 The server blocks its execution until a client connects
(lines 128 to 133).
6 The fork(2) function is called in line 139.
7 If step 6 fails, the value -1 is returned by fork(2) and the
loop repeats with step 5.
8 If step 6 succeeds, then PID will contain the process ID of the child process in the parent process (lines 144 to 147) The parent process simply closes the accepted connection
The parent process loops between steps 5 to 8 until the server
is killed off Effectively, the parent process only accepts
connections It does no other work.
Understanding the Child Server Process Flow
The child process in Listing 11.2 , created by the fork(2) process
in step 6, follows these steps:
1 Because the fork(2) function returns zero for the child
process, its code continues execution at line 153.
socket with FILE streams tx and rx (lines 153 to 164).
3 Processing continues in the child process as normal in lines 170 to 184.
the client The exit(3) function is called to terminate the child process (line 189) This step will cause the signal
SIGCHLD to be raised in the parent server process.
Trang 22Note that the parent process closes the socket c in line 145 This
client processes have this connected socket open The parent process is not servicing the client request, so it simply closes the socket The child process, however, will process the client's
With this design, the connected client can take its merry time in submitting requests without making other clients of the same server wait This is because the parent server process simply accepts new connections The parent server process performs the following steps:
1 Accept a connection from a client.
2 Fork a new process to service the client.
3 Close its copy of the connected client's socket.
4 Repeat step 1.
The servicing of the connected client is simple, because the server child process only has to worry about one connected
socket.
Understanding Process Termination Processing
The one complication that the fork(2) function call inflicts upon the design of the server is that it must process information
about terminated processes This is very important, because when a child process terminates, most of its resources are
released The rest of its resources are released only when the parent process obtains the child process termination status information
The parent process is notified of a child process termination by
parent server process uses when a child process terminates:
1 The signal SIGCHLD is raised by the kernel to indicate that the child process has terminated.
2 The function sigchld_handler() is called (line 35), because the
3 The sigchld_handler() executes a loop calling waitpid(2) until
no more exit status information is available.
4 The SIGCHLD handler is re-instated in line 44 This was
necessary because the reliable signals interface was not used in order to keep the example program simple
Trang 23In a production mode server, only the reliable signal functions
example program to keep the source code simple.
Caution
Failure to call wait(2) or waitpid(2) by the parent process after a
fork(2) and the child process's subsequent termination will result
in zombie processes being left around until the parent process
terminates This can tie up valuable system resources
waitpid(2), and signal(2), if necessary These are important aspects
of this server design.
Designing Servers That Use select(2)
function to gainfully serve multiple clients, there are other
server designs that might be preferable A server that must share information between connected clients might find it
desirable to keep the server contained within a single process Another requirement that might dictate a single process server model is the fact that one process does not consume the same amount of system resources as many processes would For
these reasons, it is necessary to consider a new server design philosophy
Introducing the select(2) Function
The select(2) function permits you to block the execution of your server until there is something for the server to do More
specifically, it permits the caller to know when
the server program.
Trang 24You will recall that the handle to a socket is a file descriptor The select(2) function will notify the server when something has happened on any one of a specified set of connected client
sockets In effect, this allows the server to process multiple clients in a very efficient manner
As pointed out previously, the server is interested when any new request data is coming in from a client's socket To know this, the server needs to know when there is data to be read from a particular client's socket.
When sending data back to the client, it is important for the server to know that it can write the data to the socket without being blocked If the connected client, for example, requests a large amount of information to be returned, the server will have
to write that information to the socket If the client software is faulty or is slow reading the data at its end, the server will block for a long time, while attempting to write the rest of the result data This has the consequence that all other clients that are connected to the server must now also wait This is clearly
undesirable, since each client must be serviced as expeditiously
as possible.
If your server must also process out-of-band data (to be covered
in Chapter 14, "Out-of-Band Data" ), then you will be interested
in exceptions that might take place on the socket.
struct timeval *timeout);
This function requires five input arguments:
value is at least the highest file descriptor number plus one, since descriptors start at zero
2 The set of file descriptors ( readfds ) that are to be tested for read data.
Trang 253 The set of file descriptors ( writefds ) that are to be tested for writability.
4 The set of file descriptors ( exceptfds ) that are to be tested for exceptions.
to be applied to this function call This pointer may be
NULL, indicating that there is no timeout (the function call may block forever).
as follows:
for the nature of the error.
occurred without anything interesting happening.
file descriptors where something of interest has occurred
The timeval Structure
initialized unless a NULL pointer is provided instead Listing 11.3
shows the definition of the timeval structure
Listing 11.3 The Definition of the timeval Structure
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
The example shows the establishing of a timeout of 1.75
seconds This is done by setting up a timeout of one second plus 750,000 microseconds.
Caution