Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 51 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
51
Dung lượng
851,42 KB
Nội dung
413: rpn_process(FILE *tx,char *buf) { 414: int z; 415: mpz_t *t; 416: char *operation; 417: char *operand; 418: 419: operation=strtok(buf,":\n\r"); 420: operand=strtok(NULL,"\n\r"); 421: 422: if ( !strcmp(operation,"dump") ) { 423: rpn_dump(tx); 424: 425: } else if ( !strcmp(operation,"=") ) { 426: /* 427: * Pop off the result: 428: */ 429: if ( (z = rpn_pop(&t)) == -1 ) 430: fputs("E:Nothing to pop\n",tx); 431: else { 432: fprintf(tx,"%d:",z); 433: mpz_out_str(tx,10,*t); 434: fputc('\n',tx); 435: rpn_free(&t); 436: } 437: 438: } else if ( !strcmp(operation,"#") ) { 439: /* 440: * Push an operand onto the stack: 441: */ 442: t = rpn_alloc(); 443: if ( !mpz_set_str(*t,operand,10) ) 444: fprintf(tx,"%d:\n",rpn_push(t)); 445: else { 446: fputs("E:Invalid number\n",tx); 447: rpn_free(&t); 448: } 449: 450: } else { 451: /* 452: * Perform an operation: 453: */ 454: z = rpn_opr(operation); 455: if ( z == -1 ) 456: fprintf(tx, 457: "E:Operation failed.\n"); 458: else 459: fprintf(tx,"%d:\n",z); 460: } 461: 462: fflush(tx); 463: } Linux Socket Programming by Example - Warren W. Gay 257 While Listing 10.5 is quite long, only the server concepts within it are important to you here. Consequently, only the rpn_process() 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. 6. If steps 3, 4, and 5 do not apply, all other RPN operations are handled by the function rpn_opr(). The result reported back to the client is the stack index value or an error indication (lines 454 to 460). 7. The output is forced to be written to the socket by calling fflush(tx) in line 462. How the server works from the client side, will be examined after the remainder of the server code is presented. Listing 10.6 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 1: /* rpnsrv.c: 2: * 3: * Example RPN Server: 4: */ 5: #include <stdio.h> 6: #include <unistd.h> 7: #include <stdlib.h> 8: #include <errno.h> 9: #include <string.h> 10: #include <time.h> 11: #include <sys/types.h> 12: #include <sys/socket.h> 13: #include <netinet/in.h> 14: #include <arpa/inet.h> 15: #include <netdb.h> 16: Linux Socket Programming by Example - Warren W. Gay 258 17: #ifndef SHUT_RDWR 18: #define SHUT_RDWR 3 19: #endif 20: 21: extern int mkaddr(void *addr, 22: int *addr_len, 23: char *input_address, 24: char *protocol); 25: 26: extern void rpn_process(FILE *tx, 27: char *buf); 28: 29: 30: /* 31: * This function reports the error and 32: * exits back to the shell: 33: */ 34: static void 35: bail(const char *on_what) { 36: if ( errno != 0 ) { 37: fputs(strerror(errno),stderr); 38: fputs(": ",stderr); 39: } 40: fputs(on_what,stderr); 41: fputc('\n',stderr); 42: exit(1); 43: } 44: 45: int 46: main(int argc,char **argv) { 47: int z; 48: char *srvr_addr = "127.0.0.1:9090"; 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: 61: */ 62: if ( argc >= 2 ) 63: srvr_addr = argv[1]; 64: 65: len_inet = sizeof adr_srvr; 66: z = mkaddr(&adr_srvr,&len_inet, 67: srvr_addr,"tcp"); 68: Linux Socket Programming by Example - Warren W. Gay 259 69: if ( z < 0 || !adr_srvr.sin_port ) { 70: fprintf(stderr,"Invalid server " 71: "address, or no port number " 72: "was specified.\n"); 73: exit(1); 74: } 75: 76: /* 77: * Create a TCP/IP socket to use: 78: */ 79: s = socket(PF_INET,SOCK_STREAM,0); 80: if ( s == -1 ) 81: bail("socket(2)"); 82: 83: /* 84: * Bind the server address: 85: */ 86: z = bind(s,(struct sockaddr *)&adr_srvr, 87: len_inet); 88: if ( z == -1 ) 89: bail("bind(2)"); 90: 91: /* 92: * Make it a listening socket: 93: */ 94: z = listen(s,10); 95: if ( z == -1 ) 96: bail("listen(2)"); 97: 98: /* 99: * Start the server loop: 100: */ 101: for (;;) { 102: /* 103: * Wait for a connect: 104: */ 105: len_inet = sizeof adr_clnt; 106: c = accept(s, 107: (struct sockaddr *)&adr_clnt, 108: &len_inet); 109: if ( c == -1 ) 110: bail("accept(2)"); 111: 112: /* 113: * Create streams: 114: */ 115: rx = fdopen(c,"r"); 116: if ( !rx ) { 117: /* Failed */ 118: close(c); 119: continue; 120: } 121: 122: tx = fdopen(dup(c),"w"); 123: if ( !tx ) { Linux Socket Programming by Example - Warren W. Gay 260 124: fclose(rx); 125: continue; 126: } 127: 128: /* 129: * Set both streams to line 130: * buffered mode: 131: */ 132: setlinebuf(rx); 133: setlinebuf(tx); 134: 135: /* 136: * Process client's requests: 137: */ 138: while ( fgets(buf,sizeof buf,rx) ) 139: rpn_process(tx,buf); 140: 141: /* 142: * Close this client's connection: 143: */ 144: fclose(tx); 145: shutdown(fileno(rx),SHUT_RDWR); 146: fclose(rx); 147: } 148: 151: } The main features of the server code in Listing 10.6 should be relatively familiar to you now. The basic steps used in this module were as follows: 1. The C macro SHUT_RDWR is defined in line 18, if the macro is not already defined. This makes the source code clearer when shutdown(2) is being called later in the program. 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. Linux Socket Programming by Example - Warren W. Gay 261 9. The I/O streams are set to line buffered mode (lines 132 to 133). 10. Until EOF is reached, each text line is read from the client and processed by the function rpn_process() (lines 138 to 139). 11.A full shutdown is performed for this client (lines 144 to 146). 12.Repeat step 7 to accommodate the next client connection. Although limited, you can see that Listing 10.6 is a simple 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, you can perform the following make command: $ 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 $ After the executable rpnsrv for the server has been created, you 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. Linux Socket Programming by Example - Warren W. Gay 262 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: This indicates that the number 2636364 was stacked at position 1, although the original number 970976453 still sits at the bottom of the stack at position 0. You can list the current contents of the stack by entering the following: dump The following example shows what the session and its output might look like this: $ telnet localhost 9090 Trying 127.0.0.1 Connected to localhost. Escape character is '^]'. #:970976453 0: #:2636364 1: dump 1:2636364 0:970976453 E:end of stack dump Linux Socket Programming by Example - Warren W. Gay 263 To perform a binary operation, you simply enter the name of the operation or its symbol. For example, to add these numbers, you would just enter the + character and press return. The session repeated without entering the dump command would appear as follows if the + operation was performed, and then followed by the = operation: $ telnet localhost 9090 Trying 127.0.0.1 Connected to localhost. Escape character is '^]'. #:970976453 0: #:2636364 1: + 0: = 0:973612817 ^] telnet> c Connection closed. $ The + operation caused the two stacked numbers to be added together, and the result replaced the two original values. The = operator here pops the result off the stack and displays it for you. To exit the server, type CTRL+] and you will be prompted with the prompt: telnet> From there, enter a c to indicate that you want the session closed, and press Enter. To terminate the server, just use the kill 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. Linux Socket Programming by Example - Warren W. Gay 264 What's Next This chapter has introduced you to the idea of using FILE 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 professional- grade servers, which can service large numbers of clients at once. Linux Socket Programming by Example - Warren W. Gay 265 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. The server, acting as the central hub in Figure 11.1, must 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 • Forked server processes (multi-process method) • Threaded server processes (multi-thread method) • One process and a select(2) call • One process and a poll(2) call The first method of using the fork(2) system call is perhaps the 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 Linux Socket Programming by Example - Warren W. Gay 266 [...]... capacity If so, close the * socket and wait for the * next event: */ if ( c >= MAX_CLIENTS ) { close(c); /* At capacity */ continue; } Linux Socket Programming by Example - Warren W Gay 289 248: 249: 250: 251: 252: 253: 254: 255: 2 56: 257: 258: 259: 260 : 261 : 262 : 263 : 264 : 265 : 266 : 267 : 268 : 269 : 270: 271: 272: 273: 274: 275: 2 76: 277: 278: 279: 280: 281: 282: 283: 284: 285: 2 86: 287: 288: 289: 290: 291:... Linux Socket Programming by Example - Warren W Gay 272 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60 : 61 : 62 : 63 : 64 : 65 : #include #include #include #include #include #include #include #include ... W Gay 274 118: 119: 120: 121: 122: 123: 124: 125: 1 26: 127: 128: 129: 130: 131: 132: 133: 134: 135: 1 36: 137: 138: 139: 140: 141: 142: 143: 144: 145: 1 46: 147: 148: if ( z == -1 ) bail("listen(2)"); 149: 150: 151: 152: 153: 154: 155: 1 56: 157: 158: 159: 160 : 161 : 162 : 163 : 164 : 165 : 166 : 167 : 168 : 169 : /* * CHILD PROCESS: * Create streams: */ rx = fdopen(c,"r"); if ( !rx ) { /* Failed */ close(c); continue;... *srvr_addr = "127.0.0.1:9090"; Linux Socket Programming by Example - Warren W Gay 273 66 : 67 : 68 : 69 : 70: 71: 72: 73: 74: 75: 76: 77: struct sockaddr_in adr_srvr;/* AF_INET */ struct sockaddr_in adr_clnt;/* AF_INET */ int len_inet; /* length */ int s = -1; /* Socket */ int c = -1; /* Client socket */ FILE *rx = NULL; /* Read stream */ FILE *tx = NULL; /* Write stream */ char buf[40 96] ; /* I/O Buffer */ pid_t... 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60 : 61 : 62 : 63 : 64 : 65 : 66 : 67 : 68 : 69 : 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: char *protocol); extern void rpn_process(FILE *tx, char *buf); extern void rpn_free(mpz_t **v); #define MAX_STACK #define MAX_CLIENTS 32 64 /* * Declared in rpneng2.c: */ extern mpz_t **stack; extern int sp; /* * Client context... Example - Warren W Gay 287 138: 139: 140: 141: 142: 143: 144: 145: 1 46: 147: 148: 149: 150: 151: 152: 153: 154: 155: 1 56: 157: 158: 159: 160 : 161 : 162 : 163 : 164 : 165 : 166 : 167 : 168 : 169 : 170: 171: 172: 173: 174: 175: 1 76: 177: 178: 179: 180: 181: 182: 183: 184: 185: 1 86: 187: 188: 189: 190: 191: 192: */ for ( z=0; z . character is '^]'. #:9709 764 53 0: #: 263 6 364 1: dump 1: 263 6 364 0:9709 764 53 E:end of stack dump Linux Socket Programming by Example - Warren W. Gay 263 To perform a binary operation, you. } 159: 160 : tx = fdopen(dup(c),"w"); 161 : if ( !tx ) { 162 : fclose(rx); 163 : continue; 164 : } 165 : 166 : /* 167 : * Set both streams to line 168 : * buffered mode: 169 : */ Linux Socket Programming. for example: #:25 0: Linux Socket Programming by Example - Warren W. Gay 271 #:9999999999999999999999999999999 1: genprime 0: dump 0:73 169 466 69 968 331 260 251308920347 E:end of stack dump This example