Simple tools for networking / objects in plain C Snapshot
Classes | Defines | Typedefs | Functions
SOCKET

a usable socket abstraction More...

Classes

struct  tagSOCKCTX

Defines

#define LINGER_OPTION_VALUE   300
#define SOCKCTX_FLAGS_NAGLE_ON   2
#define SOCKTCX_FLAGS_DONT_CLOSE_ON_PEER_CLOSE   4

Typedefs

typedef struct tagSOCKCTX SOCKCTX

Functions

int SOCK_init (SOCKCTX *ctx, int verbose, int flags)
 creates a socket and sets some options
int SOCK_attach (SOCKCTX *ctx, int fd, int verbose, int flags)
int SOCK_send_buffer_sizes (SOCKCTX *ctx, int read_buffer_size, int write_buffer_size)
 set read and write buffer sizes; a value of -1 for buffer size is ignored.
int SOCK_connect (SOCKCTX *ctx, void *addr, int addr_size, int connect_timeout)
 connects a sockets with timeout (in seconds)
int SOCK_recv (SOCKCTX *ctx, void *msg, size_t length, int read_timeout)
 read some data from a socket with timeout (in seconds)
int SOCK_recv_all (SOCKCTX *ctx, void *msg, size_t length, int read_timeout)
 read whe whole buffer from a socket with timeout (in seconds)
int SOCK_send (SOCKCTX *ctx, void *msg, size_t length, int write_timeout)
 write whe whole buffer from a socket with timeout (in seconds)
int SOCK_close (SOCKCTX *ctx)
 close the
int SOCK_close_with_reset (SOCKCTX *ctx)
 ungraceful connection termination.

Detailed Description

a usable socket abstraction

When you create a socket(2) then it is blocking by default. This means that you can't specify a timeout while using the socket - that makes it useless for any practical purpose.

This abstraction creates a non blocking socket, and you can use it in a blocking manner from a thread, alas you can specify timeouts. This way you can have 'one thread per connection' in a way that allows you to set timeouts for read, write and connect primitives.

In a way this wrapper replace one set of utterly incomprehensible default behavior (the Berkley socket api) with another set of behaviour (which maybe makes more sense, maybe it doesn't - a matter of taste, as many things are with computers)

Still, the Berkley socket interface is supposed to create the illusion, that one can do TCP networking without understanding the protocols. That is an illusion, and one simply has to read 'TCP IP Illustrated' by R. Stevens in order to correct it.


Define Documentation

#define LINGER_OPTION_VALUE   300

Definition at line 34 of file sock.h.

#define SOCKCTX_FLAGS_NAGLE_ON   2

if flag is set then nagle algorithm is enabled - this is the opposite behaviour of normal sockets where nagle is enabled by default.

The Nagle algorithm makes sense for high latency networks such as the internet; it does not make sense for low latency LAN networks. Here application performance is typically faster without the nagle algorithm. - this stuff therefore assumes that is more likely used in a local network.

The Nagle algorithm http://en.wikipedia.org/wiki/Nagle%27s_algorithm tries to combine network writes into larger packets, If the application calls send then the data is not Immediately written out; instead it is gathered with the hope of accumulating data into larger packets, these packets are then sent on the Nagle algorithm's timer.

Definition at line 46 of file sock.h.

#define SOCKTCX_FLAGS_DONT_CLOSE_ON_PEER_CLOSE   4

In 99% of all application protocols you will want to close the connection, once the peer closes it's side, and recv returns 0.

Set this option if you do not want the socket closed, once the peer closes it's side of the connection, and recv returns 0;

Setting this option means the following. Once the peer closes its connection, it is no longer able to send data over the connection, howeve the peer still expects to read outstanding data that will be send by the application, and the application will send this data after it has received the information that the peer closed its side.

Also, if this options is set, then SO_LINGER option is enabled to the value of LINGER_OPTION_VALUE This means that when the socket is closed, and there is data that has to be written still, then close will block until the outstanding data has been acknowledged by the peer.

Definition at line 62 of file sock.h.


Typedef Documentation

typedef struct tagSOCKCTX SOCKCTX

Function Documentation

int SOCK_attach ( SOCKCTX ctx,
int  fd,
int  verbose,
int  flags 
)

Definition at line 21 of file sock.c.

{
  ctx->verbose = verbose;
  ctx->connected = 0;

  ctx->addr = 0;
  ctx->addr_size = 0;

  disable_sigpipe();
 
  ctx->fd = fd;
 
 if ( fd_set_blocking( ctx->fd, 0 ) ) {
     if (ctx->verbose) {
       fprintf(stderr, "Failed to make socket non blocking errno %d\n", errno);
     }
  }

  if ((flags & SOCKCTX_FLAGS_NAGLE_ON) == 0) {  
    if ( fd_set_nagling( ctx->fd, 1 ) ) {
       if (ctx->verbose) {
         fprintf(stderr,"Failed to set nodelay option (set nodelay option). errno %d\n",errno);
       }    
    }
  }

  ctx->close_on_peer_close = 1; 
  if (flags & SOCKTCX_FLAGS_DONT_CLOSE_ON_PEER_CLOSE) {
    if ( fd_set_linger_option( ctx->fd, 1, LINGER_OPTION_VALUE) ) {
      if (ctx->verbose) {
       fprintf(stderr,"Failed to set linger option. %d\n", errno );
      }
    }
    ctx->close_on_peer_close = 0; 
  }

  return 0;
}
int SOCK_close ( SOCKCTX ctx)

close the

Parameters:
ctx- pointer to socket object. socket

Definition at line 317 of file sock.c.

{
  if (ctx->fd != -1) {
    if (close(ctx->fd)) {
      if (ctx->verbose) {
         fprintf(stderr,"Close failed errno %d\n", errno);
      }
    }
    ctx->fd = -1;
    ctx->connected = 0;
  }
  return 0;
}
int SOCK_close_with_reset ( SOCKCTX ctx)

ungraceful connection termination.

This function avoids TIME_WAIT when initiating closing of connection.

TIME_WAIT occurs, when the socket has been shut down properly; the application has sent FIN to the peer, and received an ACK for this message; the application has now sent the final last ACK to the peer; the connection is now in TIME_WAIT state and remains so for quite some time. Why? The last ACK might get lost, so TCP wants to make sure that no retransmissions of the last ACK are required.

For high performance servers this can mean that we will run out of socket handles.

The solution is for the client to terminate the connection ungracefully; instead of sending a FIN packet, the application can send out a RST (reset) packet, this will avoid the whole protocol of closing connections.

Here the SO_LINGER option is set with timeout of 0 prior to calling close; this results in sending out the RST packet / terminates the connection ungracefully.

Note that there is still a chance that the RST packet will get lost; in this we can only hope that the server will close it's side of the connection due to some connection idle timeout.

Parameters:
ctx- pointer to socket object.

Definition at line 331 of file sock.c.

{
  if (ctx->fd != -1) {
    if (fd_close_by_RST( ctx->fd )) {
      if (ctx->verbose) {
        fprintf(stderr,"Failed to set linger option. %d\n", errno );
      }
    }
  }
  ctx->fd = -1;
  ctx->connected = 0;
  return 0;
}
int SOCK_connect ( SOCKCTX ctx,
void *  addr,
int  addr_size,
int  connect_timeout 
)

connects a sockets with timeout (in seconds)

Parameters:
ctx- pointer to socket object.
addr
addr_size
connect_timeout

Definition at line 97 of file sock.c.

{
  int rt;
  fd_set wset,eset;
  struct timeval tv;
  int error = 0;
  int len;
    
  if (ctx->connected) {
    return 0;
  }

  if (addr != 0) {
     ctx->addr = malloc( addr_size );
     if (!ctx->addr) {
       goto err;
     }
     memcpy( ctx->addr, addr, addr_size );
     ctx->addr_size = addr_size;
  }

  if (!ctx->addr) {
    goto err;
  }

  do {
    rt = connect( ctx->fd, (struct sockaddr *) ctx->addr, ctx->addr_size );
  } while( rt == -1 && errno == EINTR);

  if (rt == -1) {


#if 0
     if (errno != EINPROGRESS) {
        if (ctx->verbose) {
          fprintf(stderr,"Connect syscall failed. %d\n", errno );
        }
        goto err;
     }
#endif

     FD_ZERO(&wset);
     FD_SET(ctx->fd,&wset);
     eset = wset;

     tv.tv_sec = connect_timeout;
     tv.tv_usec = 0;

     do {
       rt = select( ctx->fd + 1, 0, &wset, &eset, &tv ); 
     } while( rt == -1 && errno == EINTR);
     
     if (rt <= 0) {
        if (ctx->verbose) {
          fprintf(stderr,"Connect timed out. %d\n", errno );
        }
        goto err;
     }

     if (FD_ISSET( ctx->fd, &wset) ||  FD_ISSET( ctx->fd, &eset)) {
       len = sizeof(error);
       error = 0;
       if (getsockopt( ctx->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *) &len ) || error) {
         if (ctx->verbose) {
           fprintf(stderr,"Connect socket error. %d errno %d\n", error, errno );
         }
         goto err;
       }
     }

     if (! FD_ISSET( ctx->fd, &wset)) {
         if (ctx->verbose) {
           fprintf(stderr,"Still not connected. errno %d\n", errno );
         }
         goto err;
     }
  }


  ctx->connected = 1;
  return 0;

err:
  SOCK_close(ctx);
  return -1;
}
int SOCK_init ( SOCKCTX ctx,
int  verbose,
int  flags 
)

creates a socket and sets some options

Parameters:
ctx- pointer to socket object.
verbose
flags- bitmask of SOCKTX_FLAGS_xxx values

Definition at line 61 of file sock.c.

{
  int fd;
 
  fd = socket( PF_INET, SOCK_STREAM, 0 );
  if (fd == -1) {
     if (verbose) {
       fprintf(stderr, "Failed to create socket %d\n", errno);
     }
     return -1;
  }

  return SOCK_attach( ctx , fd, verbose, flags);
}
int SOCK_recv ( SOCKCTX ctx,
void *  msg,
size_t  length,
int  read_timeout 
)

read some data from a socket with timeout (in seconds)

Parameters:
ctx- pointer to socket object.
Returns:
either one of: -1 on error, 0 if socket has been closed, number of bytes read.

Definition at line 186 of file sock.c.

{
  int rt;
  fd_set rset;
  struct timeval tv;
     
retry:
     do {
       rt = recv( ctx->fd, msg, length, 0 );
     } while( rt == -1 && errno == EINTR );

     if (rt == -1) {
          
        if (errno != EAGAIN) {
           if (ctx->verbose) {
             fprintf(stderr,"recv failed errno %d\n", errno );
           }
           goto err;
        }

        FD_ZERO(&rset);
        FD_SET(ctx->fd,&rset);

        tv.tv_sec = read_timeout;
        tv.tv_usec = 0;

        do {
          rt = select( ctx->fd + 1, &rset, 0, 0, &tv ); 
        } while( rt == -1 && errno == EINTR);
        

        if (rt == -1) {
          if (ctx->verbose) {
            fprintf(stderr, "select failed. %d\n", errno );
          }
        }
     
        if (rt == 0) {
           if (ctx->verbose) {
             fprintf(stderr,"read timed out\n" );
           }
           goto err;
        }

        if (rt == 1) {
           goto retry;
        }

     }

   if (rt == 0 && ctx->close_on_peer_close  ) {
      SOCK_close( ctx );
   }
   return rt;

err:
   return -1;
}        
int SOCK_recv_all ( SOCKCTX ctx,
void *  msg,
size_t  length,
int  read_timeout 
)

read whe whole buffer from a socket with timeout (in seconds)

Parameters:
ctx- pointer to socket object.
Returns:
either one of: -1 on error, 0 if socket has been closed during receiving buffer, length on success.

Definition at line 245 of file sock.c.

{
  size_t pos;
  int rt;


  for(pos = 0; pos < length; ) {
      rt = SOCK_recv( ctx, msg, length, read_timeout );
      if (rt <= 0) {
        return rt;
      }
      pos += rt;
  }
  return pos;
}
int SOCK_send ( SOCKCTX ctx,
void *  msg,
size_t  length,
int  write_timeout 
)

write whe whole buffer from a socket with timeout (in seconds)

Parameters:
ctx- pointer to socket object.

Definition at line 262 of file sock.c.

{
  int rt;
  size_t pos;
  fd_set wset;
  struct timeval tv;
  char *msg = bmsg;

  for( pos = 0; pos < length; ) {

     do {
       rt = send( ctx->fd, msg + pos, length - pos, 0 );
     } while( rt == -1 && errno == EINTR );

     if (rt == -1) {
          
        if (errno != EAGAIN) {
           if (ctx->verbose) {
             fprintf(stderr,"send failed errno %d\n", errno );
           }
           goto err;
        }

        FD_ZERO(&wset);
        FD_SET(ctx->fd,&wset);

        tv.tv_sec = write_timeout;
        tv.tv_usec = 0;

        do {
          rt = select( ctx->fd + 1, 0, &wset, 0, &tv ); 
        } while( rt == -1 && errno == EINTR);
        

        if (rt == -1) {
          if (ctx->verbose) {
            fprintf(stderr, "select failed. %d\n", errno );
          }
        }
     
        if (rt == 0) {
           if (ctx->verbose) {
             fprintf(stderr,"send timed out\n" );
           }
           goto err;
        }
     }
     pos += rt;
   }
   return pos;

err:
   return -1;
}        
int SOCK_send_buffer_sizes ( SOCKCTX ctx,
int  read_buffer_size,
int  write_buffer_size 
)

set read and write buffer sizes; a value of -1 for buffer size is ignored.

Parameters:
ctx- pointer to socket object.
read_buffer_size
write_buffer_size

Definition at line 77 of file sock.c.

{
  if (read_buffer_size > 0) {
    if (fd_set_buf_size( ctx->fd, Receive_buffer, read_buffer_size)) {
      fprintf(stderr,"Failed to set receive buffer size to %d\n",read_buffer_size);
      return -1;
    }
  }
  
  if (write_buffer_size > 0) {
    if (fd_set_buf_size( ctx->fd, Send_buffer, write_buffer_size)) {
      fprintf(stderr,"Failed to set send buffer size to %d\n",write_buffer_size);
      return -1;
    }
  }
  return 0;  

}