HP OpenVMS Systems

ask the wizard
Content starts here

IP Keepalive and dropping idle connections?

» close window

The Question is:

 
I cannot get the drop idle and keep alive TCPIP socket options to work. I have
 experimented using the attached program and 2 Alpha's running the program as
 server on one Alpha and client on the other. Neither server nor client drops
 the connection after I
 physically disconnect one Alpha from the other. And if I after a while then
 physically reconnect the Alpha's then the server continues printing the
 message repeatedly sent from the client (as if nothing happened). I have also
 verified this by running ser
ver and client from the debugger. Neither side drops out of its select() after
 I disconnect one Alpha from the other. I have never yet seen select() return
 with an "exception". Is this where I would see an exception? Both Alpha's are
 running TCPIP v5.0a.
 
Any help is much appreciated.
 
(I ran the attached programs with droptime=10, probetime=-5 and reptime=60)
 
/*
 * This is a testbed for examination of TCPIP sockets.
 * It runs as server or client depending on the commandline args.
 * The server prints the messages received as sent by clients.
 * It is derived from tcpip$examples:tcp_server_ipc.c and tcp_client_ipc.c.
 *
 * The server args are
 *
 *   <local>   : Local interface
 *                 ="": any local interface
 *   <port>    : Port number
 *   <segsize> : Max size of recv() segments
 *                 =0: no segmentation
 *   <droptime>: Wait in seconds until drop of idle connection
 *                 =0: no drop time
 *
 * Examples
 *
 *   mcr <filespec> "" 5678 0 0
 *   mcr <filespec> "192.168.12.34" 5678 5 60
 *
 * The client args are
 *
 *   <local>    : Local interface
 *                  ="": any local interface
 *   <remote>   : Remote interface
 *   <port>     : Port number
 *   <message>  : Any message
 *   <segsize>  : Max size of send() segments
 *                  =0: no segmentation
 *   <probetime>: Wait in seconds until next probe
 *                  <0: enable keepalive
 *                  =0: no probe time
 *   <reptime>  : Wait in seconds until repeat
 *                  <0: do not wait
 *                  =0: no repeat time
 *
 * Examples
 *
 *   mcr <filespec> "" "192.168.12.34" 5678 "Hi there" 0 5 2
 *   mcr <filespec> "192.168.12.34" omega 5678 "Hi there" 0 0 0
 *
 */
#include <ssdef.h>              //SS$_<xyz> sys ser return stati <8-)
#include <stdio.h>              //UNIX 'Standard I/O' Definitions
#include <iodef.h>              //I/O function code defs
#include <starlet.h>            //Sys ser calls
#include <lib$routines.h>       //LIB$ RTL-routine signatures.
#include <string.h>             //String handling function definitions
#include <unixio.h>             //Prototypes for UNIX emulation functions
#include <signal.h>             //UNIX style Signal Value Definitions
#include <errno.h>              //C RTL Unix style error identifiers
#include <in.h>                 //internet system constants and structures
#include <inet.h>               //Network address info.
#include <netdb.h>              //Network database library info.
#include <socket.h>             //TCP/IP socket definitions.
#include <stdlib.h>             //General Utilities
#include <tcpip$inetdef.h>      //TCPIP network definitions
 
static int              Server();
static int              Client();
static void             Cleanup();
static void             VacateMsgBuf();
static int              GetSockTag();
 
main(
int                     argc,
char                    *argv[] )
 
 
  int                   sts, s_t_s, status;
 
 
  /*
   * Check args
   */
  if( argc == 5 )
    sts = Server( argc, argv );
  else
  if( argc == 8 )
    sts = Client( argc, argv );
  else
  {
    printf( "server args:\n" );
    printf( "  <local> <port> <segsize> <droptime>\n" );
    printf( "client args:\n" );
    printf( "  <local> <remote> <port> <message> <segsize> <probetime>
 <reptime>\n");
    sts = 0;
  }
 
  return( sts );
 
 
/*
 * Server
 */
static int Server(
int                     argc,
char                    *argv[] )
 
 
  int                   sts, s_t_s, status;
  int                   sock;
  int                   port;
  int                   optval;
  int                   flag;
  int                   cnt;
  int                   msgsiz_recv;
  int                   msgoff;
  int                   remsiz;
  int                   segsiz;
  int                   size;
  int                   sock_connect_msgsiz;
  int                   sts_select;
  int                   sock_listen;
  int                   sock_accept;
  int                   sock_connect;
  int                   wait_read;
  int                   wait_write;
  int                   wait_probe;
  int                   wait_drop;
  int                   repeat_probe;
  int unsigned          sock_mask_read;
  int unsigned          sock_mask_write;
  int unsigned          sock_mask_excep;
  int unsigned          sock_listen_mask;
  int unsigned          sock_accept_mask;
  int unsigned          sock_connect_mask;
  int unsigned          sock_read_mask;
  int unsigned          sock_write_mask;
  int unsigned          sock_accept_busy;
  int unsigned          sizeof_sock_accept_tag;
  int unsigned          sizeof_sock_connect_tag;
  char                  *local;
  char                  *remote;
  char                  *msgbuf_send;
  char                  *sock_connect_msgbuf;
  static                                        //bind fails unless static
  struct sockaddr_in    sock_listen_tag;        //tag struct for socket
  static                                        //bind fails unless static
  struct sockaddr_in    sock_accept_tag;        //tag struct for socket
  static                                        //bind fails unless static
  struct sockaddr_in    sock_connect_tag;       //tag struct for socket
  struct timeval        wait_select = { 0, 0 },
                        *wait_select_p;
  char                  msgbuf_recv[BUFSIZ];
  int                   sock_accept_msgsiz[32] = { 0 };
  char                  sock_accept_msgbuf[32][10];//small to illustrate
  struct in_addr        sock_connect_addr[32];
 
 
  /*
   * Get args
   */
  local  = argv[1];                             //local interface
  port   = atoi( argv[2] );                     //port number
  if( !port || 65535 < port )
  {
    printf( "Invalid port: %s\n", argv[2] );
    return( 0 );
  }
  segsiz = atoi( argv[3] );                     //segment size
  wait_drop = atoi( argv[4] );                  //idle connection timeout
 
  /*
   * Get socket tag of listen socket
   */
  sts = GetSockTag( local, port, &sock_listen_tag );
  if( !sts )
  {
    perror( "GetSockTag" );
    return( 0 );
  }
 
  /*
   * Create (untagged) listen socket
   */
  sock_listen = socket( AF_INET, SOCK_STREAM, 0 );
  if( sock_listen == -1 )
  {
    perror( "socket" );
    return( 0 );
  }
 
  /*
   * Enable reuse of local addresses in assignment of local addresses to the
   * listening socket. Otherwise bind() may fail with error "local address
   * already in use" on immediate rerun of the program (because of a delay in
   * TCPIP on cleaning up?). Setting the option does not also enable reuse of
   * the port number if already in use by another process. Maybe that is what
   * option SO_REUSEPORT is for.
   * (Note, that TCPIP$C_SOCKOPT as used with the $QIO implementation does
   * NOT carry the same value as SOL_SOCKET)
   */
  optval = 1;
  sts = setsockopt( sock_listen,                //socket
                    SOL_SOCKET, SO_REUSEADDR,   //option ident
                    (char *)&optval, sizeof(optval) );//option value
  if( sts == -1 )
  {
    perror( "setsockopt" );
    Cleanup( sock_listen, 0 );
    return( 0 );
  }
 
  /*
   * Assign tag to listen socket
   */
  sts = bind( sock_listen,
              (struct sockaddr *)&sock_listen_tag,
              sizeof(sock_listen_tag) );
  if( sts == -1 )
  {
    perror( "bind" );
    Cleanup( sock_listen, 0 );
    return( 0 );
  }
 
  /*
   * Listen on listen socket for connection requests from connect socket.
   */
  sts = listen( sock_listen, 5 );
  if( sts == -1 )
  {
    perror( "listen" );
    Cleanup( sock_listen, 0 );
    return( 0 );
  }
  sock_listen_mask = 1<<sock_listen;
  sock_accept_mask = 0;                         //no sockets accepted yet
 
  /*
   * Process incoming traffic
   */
  while( !0 )
  {
 
    /*
     * Vacate message buffers of idle sockets
     */
    sock_accept_busy = sock_accept_mask & sock_mask_read;
    for( sock=0; sock<32; sock++ )
    {
      if( !(sock_accept_msgsiz[sock]) )         //if buffer empty
        continue;
      if( sock_accept_busy & 1<<sock )          //if buffer busy
        continue;
      VacateMsgBuf( sock, sock_connect_addr[sock],
                    sock_accept_msgbuf[sock], &sock_accept_msgsiz[sock] );
    }
 
    /*
     * Select
     */
    sock_read_mask =   sock_listen_mask
                     | sock_accept_mask;
    sock_mask_read  = sock_read_mask;
    sock_mask_write = 0;                        //no writing in this example
    sock_mask_excep = sock_read_mask;
    if( !sock_accept_busy )                     //if no sockets were busy
      wait_select_p = 0;                        //wait until ready
    else
      wait_select_p = &wait_select;             //wait to coalesce and vacate
    sts_select = select( 32,
                         (void *)&sock_mask_read,
                         (void *)&sock_mask_write,
                         (void *)&sock_mask_excep,
                         (void *)wait_select_p );
 
    /*
     * Timeout.
     */
    if( !sts_select )
    {
      continue;
    }
 
    /*
     * Process exception
     */
    if( sock_mask_excep )
    {
      for( sock=0; sock<32; sock++ )
      {
        if( !(sock_mask_excep & 1<<sock) )
          continue;
        sizeof_sock_connect_tag = sizeof( sock_connect_tag );
        getsockname( sock,                    //get tag of peer
                     (struct sockaddr *)&sock_connect_tag,
                     &sizeof_sock_connect_tag );
        printf( "sock: %d remote: %s select: exception\n",
                sock, inet_ntoa(sock_connect_tag.sin_addr) );
      }
    }
 
    /*
     * Accept connection request on listen socket and spawn accept socket
     */
    if( sock_listen_mask & sock_mask_read )     //if connection request
    {
      sizeof_sock_connect_tag = sizeof( sock_connect_tag );
      sock_accept = accept( sock_listen,
                            (struct sockaddr *)&sock_connect_tag,//tag of peer
                            &sizeof_sock_connect_tag );
      if( sock_accept == -1 )
      {
        perror( "accept" );
        Cleanup( sock_listen, 0 );
      }
      else
      if( 32 <= sock_accept )                   //if no room in this program
      {
        printf( "sock: %d remote: %s accept: no room\n",
                sock, inet_ntoa(sock_connect_tag.sin_addr) );
        Cleanup( sock_accept, 1 );
      }
      else
      {
        sock_accept_mask |= 1<<sock_accept;     //insert
        printf( "sock: %d chnl: %d remote: %s accept: ok\n",
                sock_accept, decc$get_sdc( sock_accept ),
                inet_ntoa(sock_connect_tag.sin_addr) );
 
        /*
         * Save addr of peer for printing
         */
        sock_connect_addr[sock_accept] = sock_connect_tag.sin_addr;
 
        /*
         * Set connection idle timeout on accept socket
         * (Note, that  TCPIP$C_TCPOPT and TCPIP$C_TCP as used with the $QIO
         * implementation carry the same value as IPPROTO_TCP)
         */
        if( wait_drop )
        {
          sts = setsockopt( sock_accept,        //socket
                            IPPROTO_TCP, TCPIP$C_TCP_DROP_IDLE,//option ident
                           (char *)&wait_drop, sizeof(wait_drop) );//option value
          if( sts == -1 )
          {
            perror( "setsockopt" );
            Cleanup( sock_accept, 1 );
            sock_accept_mask &= ~(1<<sock);     //remove
          }
        }
      }
    }
 
    /*
     * Receive message from connect socket to accept socket
     */
    sock_accept_busy = sock_accept_mask & sock_mask_read;
    if( sock_accept_busy )
    {
      for( sock=0; sock<32; sock++ )
      {
        if( !( sock_accept_busy & 1<<sock ) )   //if not busy
          continue;
        if( !segsiz )                           //if no segmentation
          size = sizeof(msgbuf_recv);
        else
        if( segsiz < sizeof(msgbuf_recv) )
          size = segsiz;
        else
          size = sizeof(msgbuf_recv);
        flag = 0;
        cnt = recv( sock,
                    msgbuf_recv, size, flag );
        if( cnt == -1 )                         //if error
        {
          perror( "recv" );
          Cleanup( sock, 1 );
          sock_accept_mask &= ~(1<<sock);       //remove
        }
        else
        if( cnt == 0 )                          //peer closed
        {
          perror( "recv" );
          Cleanup( sock, 1 );
          sock_accept_mask &= ~(1<<sock);       //remove
        }
        else                                    //ok
        {
 
          /*
           * The following is for the sake of the exercise. The buffer
           * received is emptied into the dedicated buffer as space permits
           * and the dedicated buffer emptied (printed) as it becomes full.
           */
          msgoff = 0;
          while( cnt )
          {
            remsiz =   sizeof(sock_accept_msgbuf[sock])
                     - sock_accept_msgsiz[sock];
            if( remsiz == 0 )                   //no space
            {
              VacateMsgBuf( sock, sock_connect_addr[sock],//vacate
                            sock_accept_msgbuf[sock],
                            &sock_accept_msgsiz[sock] );
              continue;                         //try again
            }
            if( remsiz < cnt )                  //if not enough space
              msgsiz_recv = remsiz;
            else
              msgsiz_recv = cnt;
            memcpy(   sock_accept_msgbuf[sock]  //append
                    + sock_accept_msgsiz[sock],
                    msgbuf_recv+msgoff, msgsiz_recv );
            sock_accept_msgsiz[sock] += msgsiz_recv;
            msgoff += msgsiz_recv;
            cnt -= msgsiz_recv;
          }
        }
      }
    }
  }
 
  return( 1 );
 
 
/*
 * Client
 */
static int Client(
int                     argc,
char                    *argv[] )
 
 
  int                   sts, s_t_s, status;
  int                   sock;
  int                   port;
  int                   optval;
  int                   flag;
  int                   cnt;
  int                   msgsiz_send;
  int                   msgoff;
  int                   remsiz;
  int                   segsiz;
  int                   size;
  int                   sock_connect_msgsiz;
  int                   sts_select;
  int                   sock_listen;
  int                   sock_accept;
  int                   sock_connect;
  int                   wait_read;
  int                   wait_write;
  int                   wait_probe;
  int                   wait_drop;
  int                   repeat_probe;
  int unsigned          sock_mask_read;
  int unsigned          sock_mask_write;
  int unsigned          sock_mask_excep;
  int unsigned          sock_listen_mask;
  int unsigned          sock_accept_mask;
  int unsigned          sock_connect_mask = 0;
  int unsigned          sock_read_mask;
  int unsigned          sock_write_mask;
  int unsigned          sock_accept_busy;
  int unsigned          sizeof_sock_accept_tag;
  int unsigned          sizeof_sock_connect_tag;
  char                  *local;
  char                  *remote;
  char                  *msgbuf_send;
  char                  *sock_connect_msgbuf;
  static                                        //bind fails unless static
  struct sockaddr_in    sock_listen_tag;        //tag struct for socket
  static                                        //bind fails unless static
  struct sockaddr_in    sock_accept_tag;        //tag struct for socket
  static                                        //bind fails unless static
  struct sockaddr_in    sock_connect_tag;       //tag struct for socket
  struct timeval        wait_select = { 0, 0 },
                        *wait_select_p;
  char                  msgbuf_recv[BUFSIZ];
  int                   sock_accept_msgsiz[32] = { 0 };
  char                  sock_accept_msgbuf[32][10];//small to illustrate
  struct in_addr        sock_connect_addr[32];
 
 
  /*
   * Get args
   */
  local        = argv[1];
  remote       = argv[2];
  port         = atoi( argv[3] );
  if( !port || 65535 < port )
  {
    printf( "Invalid port: %s\n", argv[2] );
    return( 0 );
  }
  sock_connect_msgbuf = argv[4];
  sock_connect_msgsiz = strlen( sock_connect_msgbuf );
  segsiz = atoi( argv[5] );
  wait_probe = atoi( argv[6] );
  if( wait_probe < 0 )                          //if enable keepalive
  {
    wait_probe *= -1;                           //make positive
    repeat_probe = 1;                           //enable keepalive
  }
  else
    repeat_probe = 0;                           //do not enable keepalive
  wait_write = atoi( argv[7] );                 //wait until repeat
 
 
  /*
   * Get socket tag of connect socket
   */
  sts = GetSockTag( local, 0, &sock_connect_tag );
  if( !sts )
  {
    perror( "GetSockTag" );
    return( 0 );
  }
 
  /*
   * Get socket tag of listen socket
   */
  sts = GetSockTag( remote, port, &sock_listen_tag );
  if( !sts )
  {
    perror( "GetSockTag" );
    return( 0 );
  }
 
  /*
   * Create (untagged) connect socket
   */
  sock_connect = socket( AF_INET, SOCK_STREAM, 0 );
  if( sock_connect == -1 )
  {
    perror( "socket" );
    return( 0 );
  }
 
  /*
   * Set wait until connection timeout (and keepalive, if enabled)
   * (Note, that TCPIP$C_TCPOPT and TCPIP$C_TCP as used with the $QIO
   * implementation carry the same value as IPPROTO_TCP)
   */
  if( wait_probe )
  {
    sts = setsockopt( sock_connect,             //socket
                      IPPROTO_TCP, TCPIP$C_TCP_PROBE_IDLE,//option ident
                      (char *)&wait_probe, sizeof(wait_probe) );//option value
    if( sts == -1 )
    {
      perror( "setsockopt" );
      Cleanup( sock_connect, 0 );
      return( 0 );
    }
  }
 
  /*
   * Enable keepalive e.g. enable repeat of probe, if any
   * (Note, that TCPIP$C_KEEPALIVE as used with the $QIO implementation
   * carry the same value as SO_KEEPALIVE)
   */
  if( repeat_probe )
  {
    optval = repeat_probe;
    sts = setsockopt( sock_connect,             //socket
                      SOL_SOCKET, SO_KEEPALIVE, //option ident
                      (char *)&optval, sizeof(optval) );//option value
    if( sts == -1 )
    {
      perror( "setsockopt" );
      Cleanup( sock_connect, 0 );
      return( 0 );
    }
  }
 
  /*
   * Assign tag to connect socket
   */
  sts = bind( sock_connect,
              (struct sockaddr *)&sock_connect_tag,
              sizeof(sock_connect_tag) );
  if( sts == -1 )
  {
    perror( "bind" );
    Cleanup( sock_connect, 0 );
    return( 0 );
  }
 
  /*
   * Connect connect socket to listen socket.
   */
  sts = connect( sock_connect,
                 (struct sockaddr *)&sock_listen_tag,//socket tag of peer
                 sizeof(sock_listen_tag) );
  if( sts == -1 )
  {
    perror( "connect" );
    Cleanup( sock_connect, 0 );
    return( 0 );
  }
  printf( "sock: %d chnl: %d connect: ok\n",
          sock_connect, decc$get_sdc( sock_connect ) );
  sock_connect_mask |= 1<<sock_connect;         //insert
  msgbuf_send = sock_connect_msgbuf;            //init
  msgsiz_send = sock_connect_msgsiz;
 
  /*
   * Process outgoing traffic
   */
  while( !0 )
  {
    if( !msgsiz_send ) {                        //if nothing to send
      msgbuf_send = sock_connect_msgbuf;        //reload
      msgsiz_send = sock_connect_msgsiz;
      if( wait_write < 0 )                      //if do not wait
      {
        sock_write_mask = sock_connect_mask;
        wait_select_p = 0;                      //wait until ready
      }
      else
      if( !wait_write )                         //if do not repeat
      {
        break;
      }
      else
      {
        sock_write_mask = 0;
        wait_select.tv_sec = wait_write;
        wait_select_p = &wait_select;           //wait until timeout
      }
    }
    else
    {
      sock_write_mask = sock_connect_mask;
      wait_select_p = 0;                        //wait until ready
    }
 
    /*
     * Select
     */
    sock_mask_read  = 0;                        //no reading in this example
    sock_mask_write = sock_write_mask;
    sock_mask_excep = sock_connect_mask;
    sts_select = select( 32,
                         (void *)&sock_mask_read,
                         (void *)&sock_mask_write,
                         (void *)&sock_mask_excep,
                         (void *)wait_select_p );
 
    /*
     * Timeout.
     */
    if( !sts_select )
    {
      continue;
    }
 
    /*
     * Process exception
     */
    if( sock_mask_excep )
    {
      perror( "select" );
    }
 
    /*
     * Send message from connect socket to accept socket
     */
    if( sock_connect_mask & sock_mask_write )
    {
      if( !segsiz )
        size = msgsiz_send;
      else
      if( segsiz < msgsiz_send )
        size = segsiz;
      else
        size = msgsiz_send;
      flag = 0;
      cnt = send( sock_connect,
                  msgbuf_send, size, flag );
      if( cnt == -1 )                           //if error
      {
        perror( "send" );
        Cleanup( sock_connect, 1 );
        sock_connect_mask &= ~(1<<sock_connect);//remove
        return( 0 );
      }
      else
      {
        msgbuf_send += cnt;
        msgsiz_send -= cnt;
      }
    }
  }
 
  /*
   * Call cleanup to shutdown and close socket.
   */
  Cleanup( sock_connect, 1 );
 
  return( 1 );
 
 
static
int GetSockTag(
char                    *name,
int                     port,
struct sockaddr_in      *sock_tag )             //tag struct for socket
 
 
  int                   sts, s_t_s, status;
  int                   idx;
  int                   family;
  int                   addr;
  struct hostent        *hostent;               //pointer to host file entry
 
 
  /*
   * Get value of members of tag structure of listen socket
   */
  if( !strlen( name ) )
  {
 
    /*
     * Interface not specified e.g. listen using any local interface
     */
    family = AF_INET;                           //the only legal value
    addr   = INADDR_ANY;                        //any local interface
  }
  else
  {
 
    /*
     * Interface specified e.g. listen using specified local interface only
     */
    addr = 0;
    for( idx=0; !addr; idx++ )
    {
      switch( idx ) {
      case 0:                                   //try by name in host file
        hostent = gethostbyname( name );
        if( hostent )
        {
          family = hostent->h_addrtype;         //AF_INET
          addr   = *(int *)hostent->h_addr_list[0];//first one in host file
        }
        break;
      case 1:                                   //try by addr in host file
        hostent = gethostbyaddr( name, strlen(name), AF_INET );
        if( hostent )
        {
          family = hostent->h_addrtype;         //AF_INET
          addr   = *(int *)hostent->h_addr_list[0];//first one in host file
        }
        break;
      case 2:                                   //try by addr ignoring host file
        family = AF_INET;                       //the only legal value
        addr = inet_addr( name );
        if( addr == -1 )
          addr = 0;
        break;
      default:
        return( 0 );
      }
    }
  }
 
  /*
   * Fill in members of tag structure of listen socket
   */
  sock_tag->sin_family = family;                //AF_INET
  sock_tag->sin_addr   = *(struct in_addr *)&addr;//IP address
  sock_tag->sin_port   = htons( port );         //port number
 
  return( 1 );
 
 
static
void Cleanup(
int             sock,
int             shut )
 
 
  int                   sts, s_t_s, status;
 
 
  /*
   * Shutdown socket
   */
  if( shut )
  {
    sts = shutdown( sock, 2 );
    if( sts == -1 )
      perror( "shutdown" );
  }
 
  /*
   * Close socket
   */
  sts = close( sock );
  if( sts == -1 )
    perror( "close" );
 
  return;
 
 
static
void VacateMsgBuf(
int                     sock,
struct in_addr          sin_addr,
char                    *msgbuf,
int                     *msgsiz )
 
 
  int                   sts, s_t_s, status;
 
 
  /*
   * Print message contents
   */
  printf( "sock: %d remote: %s recv: %*.*s\n",
          sock, inet_ntoa(sin_addr), *msgsiz, *msgsiz, msgbuf );
  *msgsiz = 0;                                  //now empty
 
  return;
 
 
 
 
 


The Answer is :

 
  Please contact the Compaq Customer Support Center for assistance with
  this question.
 

answer written or last revised on ( 30-NOV-2000 )

» close window