1. Trang chủ
  2. » Công Nghệ Thông Tin

Linux Socket Programming by Example PHẦN 6 pot

51 297 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Linux Socket Programming by Example PHẦN 6 pot
Trường học Warren W. Gay's University
Chuyên ngành Computer Science
Thể loại Lecture Notes
Định dạng
Số trang 51
Dung lượng 851,42 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

413: rpn_process(FILE *tx,char *buf) {

Trang 2

While 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 3

31: * 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 5

relatively 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 6

9 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 7

The 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 8

To 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 9

What'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 10

Understanding 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 11

message 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 12

The 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 13

1 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 14

9 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 15

Table 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 16

E: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 17

48: * This function reports the error and

49: * exits back to the shell:50: */

Trang 18

66: 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 19

136: * Fork a new server process

137: * to service this client: >

Trang 20

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

$ /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 22

Note 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 23

In 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 24

You 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 25

3 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

Ngày đăng: 12/08/2014, 21:20

TỪ KHÓA LIÊN QUAN