18.6 UICI Clients

Team-FLY

Program 18.3 shows the client side of the file copy. The client connects to the desired port on a specified host by calling u_connect . The u_connect function returns the communication file descriptor. The client reads the information from standard input and copies it to the server. The client exits when it receives end-of-file from standard input or if it encounters an error while writing to the server.

Program 18.3 client.c

A client that uses UICI for communication .

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "restart.h" #include "uici.h" int main(int argc, char *argv[]) {    int bytescopied;    int communfd;    u_port_t portnumber;    if (argc != 3) {       fprintf(stderr, "Usage: %s host port\n", argv[0]);       return 1;    }    portnumber = (u_port_t)atoi(argv[2]);    if ((communfd = u_connect(portnumber, argv[1])) == -1) {       perror("Failed to make connection");       return 1;    }    fprintf(stderr, "[%ld]:connected %s\n", (long)getpid(), argv[1]);    bytescopied = copyfile(STDIN_FILENO, communfd);    fprintf(stderr, "[%ld]:sent %d bytes\n", (long)getpid(), bytescopied);    return 0; } 
Exercise 18.13

How would you use Programs 18.1 and 18.3 to transfer information from one machine to another?

Answer:

Compile the server of Program 18.1 as server . First, start the server listening on a port (say 8652) by executing the following command.

 server 8652 

Compile Program 18.3 as client . If the server is running on usp.cs.utsa.edu , start the client on another machine with the following command.

 client usp.cs.utsa.edu 8652 

Once the client and server have established a connection, enter text on the standard input of the client and observe the server output. Enter the end-of-file character (usually Ctrl-D). The client terminates, and both client and server print the number of bytes transferred. Be sure to replace usp.cs.utsa.edu with the host name of your server.

Exercise 18.14

How would you use Programs 18.1 and 18.3 to transfer the file t.in on one machine to the file t.out on another? Will t.out be identical to t.in ? What happens to the messages displayed by the client and server?

Answer:

Use I/O redirection. Start the server of Program 18.1 on the destination machine (say, usp.cs.utsa.edu ) by executing the following command.

 server 8652 > t.out 

Start the client of Program 18.3 on the source machine by executing the following command.

 client usp.cs.utsa.edu 8652 < t.in 

Be sure to substitute your server's host name for usp.cs.utsa.edu . The source and destination files should have identical content. Since the messages are sent to standard error, which is not redirected, these messages still appear in the usual place on the two machines.

The client and server programs presented so far support communication only from the client to the server. In many client-server applications, the client sends a request to the server and then waits for a response.

Exercise 18.15

How would you modify the server of Program 18.1 to produce a server called reflectserver that echoes its response back to the client, rather than to standard output?

Answer:

The only modification needed would be to replace the reference to STDOUT_FILENO with communfd .

Program 18.4 is a client program that can be used with the server of Exercise 18.15. The reflectclient.c sends a fixed-length message to a server and expects that message to be echoed back. Program 18.4 checks to see that it receives exactly the same message that it sends.

Program 18.4 reflectclient.c

A client that sends a fixed-length test message to a server and checks that the reply is identical to the message sent .

 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "restart.h" #include "uici.h" #define BUFSIZE 1000 int main(int argc, char *argv[]) {    char bufrecv[BUFSIZE];    char bufsend[BUFSIZE];    int bytesrecvd;    int communfd;    int i;    u_port_t portnumber;    int totalrecvd;    if (argc != 3) {       fprintf(stderr, "Usage: %s host port\n", argv[0]);       return 1;    }    for (i = 0; i < BUFSIZE; i++)                    /* set up a test message */       bufsend[i] = (char)(i%26 + 'A');    portnumber = (u_port_t)atoi(argv[2]);    if ((communfd = u_connect(portnumber, argv[1])) == -1) {       perror("Failed to establish connection");       return 1;    }    if (r_write(communfd, bufsend, BUFSIZE) != BUFSIZE) {       perror("Failed to write test message");       return 1;    }    totalrecvd = 0;    while (totalrecvd < BUFSIZE) {       bytesrecvd = r_read(communfd, bufrecv + totalrecvd, BUFSIZE - totalrecvd);       if (bytesrecvd <= 0) {          perror("Failed to read response message");          return 1;       }       totalrecvd += bytesrecvd;    }    for (i = 0; i < BUFSIZE; i++)       if (bufsend[i] != bufrecv[i])          fprintf(stderr, "Byte %d read does not agree with byte written\n", i);    return 0; } 

Many client-server applications require symmetric bidirectional communication between client and server. The simplest way to incorporate bidirectionality is for the client and the server to each fork a child to handle the communication in the opposite direction.

Example 18.16

To make the client in Program 18.3 bidirectional, declare an integer variable, child , and replace the line

 bytescopied = copyfile(STDIN_FILENO, communfd); 

with the following code segment.

 if ((child = fork()) == -1) {    perror("Failed to fork a child");    return 1; } if (child == 0)                                           /* child code */    bytescopied = copyfile(STDIN_FILENO, communfd); else                                                     /* parent code */    bytescopied = copyfile(communfd, STDOUT_FILENO); 
Exercise 18.17

Suppose we try to make a bidirectional serial server from Program 18.1 by declaring an integer variable called child and replacing the following line with the replacement code of Example 18.16.

 bytescopied = copyfile(communfd, STDOUT_FILENO); 

What happens?

Answer:

This approach has several flaws. Both the parent and child return to the u_accept loop after completing the transfer. While copying still works correctly, the number of processes grows each time a connection is made. After the first connection completes, two server processes accept client connections. If two server connections are active, characters entered at standard input of the server go to one of the two connections. The code also causes the process to exit if fork fails. Normally, the server should not exit on account of a possibly temporary problem.

Example 18.18

To produce a bidirectional serial server, replace the copyfile line in Program 18.1 with the following code.

 int child; child = fork(); if ((child = fork()) == -1)    perror("Failed to fork second child"); else if (child == 0) {                                        /* child code */    bytescopied = copyfile(STDIN_FILENO, communfd);    fprintf(stderr, "[%ld]:sent %d bytes\n", (long)getpid(), bytes_copied);    return 0; } bytescopied = copyfile(communfd, STDOUT_FILENO);              /* parent code */ fprintf(stderr, "[%ld]:received %d bytes\n", (long)getpid(), bytescopied); r_wait(NULL); 

The child process exits after printing its message. The original process waits for the child to complete before continuing and does not accept a new connection until both ends of the transmission complete. If the fork fails, only the parent communicates.

Exercise 18.19

The modified server suggested in Example 18.18 prints out the number of bytes transferred in each direction. How would you modify the code to print a single number giving the total number of bytes transferred in both directions?

Answer:

This modification would not be simple because the values for transfer in each direction are stored in different processes. You can establish communication by inserting code to create a pipe before forking the child. After it completes, the child could write to the pipe the total number of bytes transferred to the parent.

Exercise 18.20

Suppose that the child of Example 18.18 returns the number of bytes transferred and the parent uses the return value from the status code to accumulate the total number of bytes transferred. Does this approach solve the problem posed in Exercise 18.19?

Answer:

No. Only 8 bits are typically available for the child's return value, which is not large enough to hold the number of bytes transferred.

Another way to do bidirectional transfer is to use select or poll as shown in Program 4.13 on page 111. The copy2files program copies bytes from fromfd1 to tofd1 and from fromfd2 to tofd2 , respectively, without making any assumptions about the order in which the bytes become available in the two directions. You can use copy2files by replacing the copyfile line in both server and client with the following code.

 bytescopied = copy2files(communfd, STDOUT_FILENO, STDIN_FILENO, communfd); 

Program 18.5 shows the bidirectional client.

Exercise 18.21

How does using copy2files differ from forking a child to handle communication in the opposite direction?

Answer:

The copy2files function of Program 4.13 terminates both directions of communication if either receives an end-of-file from standard input or if there is an error in the network communication. The child method allows communication to continue in the other direction after one side is closed. You can modify copy2files to keep a flag for each file descriptor indicating whether the descriptor has encountered an error or end-of-file. Only active descriptors would be included in each iteration of select .

Program 18.5 client2.c

A bidirectional client .

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "uici.h" #include "restart.h" int main(int argc, char *argv[]) {    int bytescopied;    int communfd;    u_port_t portnumber;    if (argc != 3) {       fprintf(stderr, "Usage: %s host port\n", argv[0]);       return 1;    }    portnumber = (u_port_t)atoi(argv[2]);    if ((communfd = u_connect(portnumber, argv[1])) == -1) {       perror("Failed to establish connection");       return 1;    }    fprintf(stderr, "[%ld]:connection made to %s\n", (long)getpid(), argv[1]);    bytescopied = copy2files(communfd, STDOUT_FILENO, STDIN_FILENO, communfd);    fprintf(stderr, "[%ld]:transferred %d bytes\n", (long)getpid(), bytescopied);    return 0; } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net