|
|
This server listens for connection requests from arbitrary clients. When it receives a connection request, it spawns a child process to respond to the client. The parent process continues to listen for additional connection requests.
#include <xti.h> #include other needed header filesextern int t_errno;
main (int argc, char *argv[]) {
/* Declare data structures needed for operations on the transport provider endpoint. */
/* Declare the data structure that contains the well-known address of the server. Both the type and the value of "server_address" must be transport-specific. This example assumes that they have already been defined appropriately (using "typedef" and "#define", for example). TRANSPORT_ADDRESS server_address = WELL_KNOWN_SERVER_ADDRESS;
/* Declare other data structures. */
/* Open the transport provider, using the device name assigned to that transport provider, and receive in return a file descriptor denoting that endpoint. */ fd = t_open(device_name, ... );
/* Allocate the bind data structure that will contain the well-known transport-specific address of the server. */ requested_binding = t_alloc(fd, T_BIND, T_ADDR);
/* Allocate the bind data structure that will contain the actual address assigned by the transport provider. */ actual_binding = t_alloc(fd, T_BIND, T_ADDR);
/* Fill in the bind data structure with the well-known transport-specific address of the server, and the length of the queue to hold incoming connection requests. */ requested_binding->addr.len = sizeof(server_address); requested_binding->addr.buf = &server_address; requested_binding->qlen = MAX_QUEUE_LENGTH;
/* Bind the well-known transport-specific address of the server to the transport endpoint. */ t_bind(fd, requested_binding, actual_binding);
/* Compare the requested address in "requested_binding" against the actual assigned address in "actual binding". If they don't match, issue an error and exit. */ if (memcmp(requested_binding->addr.buf, actual_binding->addr.buf, ADDRESS_LENGTH) != 0 ) { perror("Unable to bind correct server address."); exit(1); }
/* Allocate the data structure needed to record the address of the next client requesting a connection. */ call = t_alloc(fd, T_CALL, T_ADDR);
/* This server runs forever. */ while (true) {
/* Listen forever for the next connection request from a client. */ t_listen(fd, call);
/* Open a new file descriptor through which the server will complete the connection from the client. By completing the connection through "resfd", the server leaves "fd" available to receive additional connection requests from other clients. */ resfd = t_open(device_name, ... );
/* Let the transport provider select an arbitrary transport address for the new file descriptor. */ t_bind(resfd, NULL, NULL);
/* Accept the connection request that arrived at "fd" and attach the server-side of the connection to the responding file descriptor "resfd". The file descriptor "fd" continues to be available to the server to listen for new incoming connection requests from clients (see "t_listen" at the top of the loop). The "call" data structure has the information that the child server process (see the "switch" statement below) needs to communicate with the client at the other end of the connection. */ t_accept(fd, resfd, call);
/* Spawn a child server process. The parent process will continue to listen on the original file descriptor "fd" (which is still bound to the server's well-known address) for additional connection requests. The child process will communicate with the client using the "resfd" file descriptor. */ switch (fork()) {
case -1: perror("Fork of server process to respond to client request has failed. Server aborting..."); exit(1);
default: /* This is the code executed by the parent process that continues to listen on the well-known transport address for additional incoming connection requests from clients. The parent process has no use for the file descriptor to be used to communicate with the client (resfd), so it closes it. Doing so does not close this file descriptor for the child process, however, which can still use it. */ t_close(resfd);
case 0: /* This is the code executed by the child process that services the request of the client. The child process has no further use for the original file descriptor (fd) on which the connection request arrived, so it closes it. This does not close the file descriptor for the parent process, however, which will continue to listen on the "fd" file descriptor for new connection requests from clients. t_close(fd);
/* The child process can now perform some useful service for the client. In this example, the server has a very accurate clock, so it returns the correct time of day to the client, then exits. */ gettimeofday(&time_value, &timezone); t_snd(resfd, &time_value, sizeof(struct timeval), flags); t_close(resfd); exit(0); } /* end switch */
} /* end while */
} /* end main */