[ Team LiB ] |
5.18 Data FormatIn our example, the server never examines the request that it receives from the client. The server just reads all the data up through and including the newline and sends it back to the client, looking for only the newline. This is an exception, not the rule, and normally we must worry about the format of the data exchanged between the client and server. Example: Passing Text Strings between Client and ServerLet's modify our server so that it still reads a line of text from the client, but the server now expects that line to contain two integers separated by white space, and the server returns the sum of those two integers. Our client and server main functions remain the same, as does our str_cli function. All that changes is our str_echo function, which we show in Figure 5.17. Figure 5.17 str_echo function that adds two numbers.tcpcliserv/str_ech08.c 1 #include "unp.h" 2 void 3 str_echo(int sockfd) 4 { 5 long arg1, arg2; 6 ssize_t n; 7 char line[MAXLINE]; 8 for ( ; ; ) { 9 if ( (n = Readline(sockfd, line, MAXLINE)) == 0) 10 return; /* connection closed by other end */ 11 if (sscanf(line, "%ld%ld", &arg1, &arg2) == 2) 12 snprintf(line, sizeof(line), "%ld\n", arg1 + arg2); 13 else 14 snprintf(line, sizeof(line), "input error\n"); 15 n = strlen(line); 16 Writen(sockfd, line, n); 17 } 18 } 11–14 We call sscanf to convert the two arguments from text strings to long integers, and then snprintf is called to convert the result into a text string. This new client and server work fine, regardless of the byte ordering of the client and server hosts. Example: Passing Binary Structures between Client and ServerWe now modify our client and server to pass binary values across the socket, instead of text strings. We will see that this does not work when the client and server are run on hosts with different byte orders, or on hosts that do not agree on the size of a long integer (Figure 1.17). Our client and server main functions do not change. We define one structure for the two arguments, another structure for the result, and place both definitions in our sum.h header, shown in Figure 5.18. Figure 5.19 shows the str_cli function. Figure 5.18 sum.h header.tcpcliserv/sum.h 1 struct args { 2 long arg1; 3 long arg2; 4 }; 5 struct result { 6 long sum; 7 }; Figure 5.19 str_cli function which sends two binary integers to server.tcpcliserv/str_cli09.c 1 #include "unp.h" 2 #include "sum.h" 3 void 4 str_cli(FILE *fp, int sockfd) 5 { 6 char sendline[MAXLINE]; 7 struct args args; 8 struct result result; 9 while (Fgets(sendline, MAXLINE, fp) != NULL) { 10 if (sscanf(sendline, "%ld%ld", &args.arg1, &args.arg2) != 2) { 11 printf("invalid input: %s", sendline); 12 continue; 13 } 14 Writen(sockfd, &args, sizeof(args)); 15 if (Readn(sockfd, &result, sizeof(result)) == 0) 16 err_quit("str_cli: server terminated prematurely"); 17 printf("%ld\n", result.sum); 18 } 19 } 10–14 sscanf converts the two arguments from text strings to binary, and we call writen to send the structure to the server. 15–17 We call readn to read the reply, and print the result using printf. Figure 5.20 shows our str_echo function. Figure 5.20 str_echo function that adds two binary integers.tcpcliserv/str_ech09.c 1 #include "unp.h" 2 #include "sum.h" 3 void 4 str_echo(int sockfd) 5 { 6 ssize_t n; 7 struct args args; 8 struct result result; 9 for ( ; ; ) { 10 if ( (n = Readn(sockfd, &args, sizeof(args))) == 0) 11 return; /* connection closed by other end */ 12 result.sum = args.arg1 + args.arg2; 13 Writen(sockfd, &result, sizeof (result)); 14 } 15 } 9–14 We read the arguments by calling readn, calculate and store the sum, and call writen to send back the result structure. If we run the client and server on two machines of the same architecture, say two SPARC machines, everything works fine. Here is the client interaction:
But when the client and server are on two machines of different architectures (say the server is on the big-endian SPARC system freebsd and the client is on the little endian Intel system linux), it does not work.
The problem is that the two binary integers are sent across the socket in little-endian format by the client, but interpreted as big-endian integers by the server. We see that it appears to work for positive integers but fails for negative integers (see Exercise 5.8). There are really three potential problems with this example:
There are two common solutions to this data format problem:
|
[ Team LiB ] |