Transport Interfaces Programming Guide

Server

The server manages its data transfer by spawning a child process to send the data to the client. The parent process continues the loop to listen for more connect requests. run_server() is called by the server to spawn this child process, as shown in Example 3-7.


Example 3-7 Spawning Child Process to Loopback and Listen


connrelease()
{
   /* conn_fd is global because needed here */
   if (t_look(conn_fd) == T_DISCONNECT) {
      fprintf(stderr, "connection aborted\n");
      exit(12);
   }
   /* else orderly release request - normal exit */
   exit(0);
}
run_server(listen_fd)
int listen_fd;
{
   int nbytes;
   FILE *logfp;                    /* file pointer to log file */
   char buf[1024];

   switch(fork()) {
   case -1:
      perror("fork failed");
      exit(20);
   default:									/* parent */
      /* close conn_fd and then go up and listen again*/
      if (t_close(conn_fd) == -1) {
         t_error("t_close failed for conn_fd");
         exit(21);
      }
      return;
   case 0:                        /* child */
      /* close listen_fd and do service */
      if (t_close(listen_fd) == -1) {
         t_error("t_close failed for listen_fd");
         exit(22);
      }
      if ((logfp = fopen("logfile", "r")) == (FILE *) NULL) {
         perror("cannot open logfile");
         exit(23);
      }
      signal(SIGPOLL, connrelease);
      if (ioctl(conn_fd, I_SETSIG, S_INPUT) == -1) {
         perror("ioctl I_SETSIG failed");
         exit(24);
      }
      if (t_look(conn_fd) != 0){      /*disconnect there?*/
         fprintf(stderr, "t_look: unexpected event\n");
         exit(25);
      }
      while ((nbytes = fread(buf, 1, 1024, logfp)) > 0)
         if (t_snd(conn_fd, buf, nbytes, 0) == -1) {
            t_error("t_snd failed");
            exit(26);
         }

After the fork, the parent process returns to the main listening loop. The child process manages the newly established transport connection. If the fork fails, exit() closes both transport endpoints, sending a disconnect request to the client, and the client's t_connect() call fails.

The server process reads 1024 bytes of the log file at a time and sends the data to the client using t_snd(). buf points to the start of the data buffer, and nbytes specifies the number of bytes to transmit. The fourth argument can be zero or one of the two optional flags below:

Neither flag is set by the server in this example.

If the user floods the transport provider with data, t_snd() blocks until enough data is removed from the transport.

t_snd() does not look for a disconnect request (showing that the connection was broken). If the connection is aborted, the server should be notified, since data can be lost. One solution is to call t_look() to check for incoming events before each t_snd() call or after a t_snd() failure. The example has a cleaner solution. The I_SETSIG ioctl() lets a user request a signal when a specified event occurs. See the streamio(7I) manpage. S_INPUT causes a signal to be sent to the user process when any input arrives at the endpoint conn_fd. If a disconnect request arrives, the signal-catching routine (connrelease()) prints an error message and exits.

If the server alternates t_snd() and t_rcv() calls, it can use t_rcv() to recognize an incoming disconnect request.