Peer-to-Peer and Interface Protocols using C

Posted on by on March 5th, 2014 | 0 Comments »

Overview

C code to support a half-duplex layered communication system.  Doxygen documentation can be found here.

Compiling, Testing, and Running

Normall file structure is to keep all source code in a src directory and binaries in a bin directory.  Layers can be used by different client and server implementations, but example implementations have been provided. Compile with:
  • gcc -Wall -o ../bin/client client.c;
  •  gcc -Wall -o ./server server.c
Test with:
  • ./client | ./server; cat out.list; rm out.list;

Code

layer.c

/* #ifndef __UTILITY_H__ */
/* #define __UTILITY_H__ */
#include <stdio.h>
#include <string.h>
//------------------------------------------------------------------ :Functions:
//                      ___             _   _
//                     | __|  _ _ _  __| |_(_)___ _ _  ___
//                     | _| || | ' \/ _|  _| / _ \ ' \(_-<
//                     |_| \_,_|_||_\__|\__|_\___/_||_/__/
//
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Used for testing, prints a pretty formatted title a specified file.
 *  @param&#91;in&#93; num 0 for first header, 1 for any other header
 *  @param&#91;in&#93; mode The file to write to
 *  @param&#91;in&#93; title The string to print in the title
 *  @return void
 */
////////////////////////////////////////////////////////////////////////////////
extern void print_title( int num, FILE * mode, char* title )
{
    fprintf(mode, "\n----------------------------------------\n");
    fprintf(mode, "  ");
    if(num == 0)
    {
	fprintf(mode, "START");
    }
    else
    {
	fprintf(mode, " END ");
    }
    fprintf(mode, " %s\n", title);
    fprintf(mode, "----------------------------------------\n");
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Used fast exponentiation to calculate powers.
 *  @param&#91;in&#93; x The base.
 *  @param&#91;in&#93; y The power.
 *  @return x^y.
 */
////////////////////////////////////////////////////////////////////////////////
int my_pow(int x, int n)
{
    if(n < 0 )
	return my_pow(1/x, -n);
    else if ( n == 0 )
	return 1;
    else if ( n == 1 )
	return x;
    else if ( n%2 == 0 )
	return my_pow(x*x, n/2);
    else if ( n%2 != 0 )
	return x * my_pow(x*x, (n-1)/2);
    else
	return -1;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Lazy Binary number calculation
 *  @param&#91;in&#93; num The number to calculate binary of, i.e. 4 = 2^3 = 8
 *  @return 2^num
 */
////////////////////////////////////////////////////////////////////////////////
int bin_exp(int num)
{
    return my_pow(2, num);
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Converts a integer to a string
 *  @param&#91;out&#93; o_buff The correctly sized buffer to store the string in.
 *  @param&#91;in&#93; num The number to convert to a string.
 *  @param&#91;in&#93; len The size of the zero padded string to produce.
 *  @return
 */
////////////////////////////////////////////////////////////////////////////////
void itozstring( char * o_buff, int num, int len )
{
    ////////////////////
    // Variables
    ////////////////////
    int pnum;                      /* the power number */
    int fnum;                      /* the fake number */
    int rnum;                      /* the real number */
    int i=0;                       /* for loop navigation variable */
    int pos = 0;                   /* o_buff navigation variable */

    // i represents 10^i
    for ( i = len-1; i >= 0; i-- )
    {
	pnum = my_pow(10, i);       /* calculate 10^i (i.e. 10^4 = 1000) */
	fnum = num/pnum;            /* calculate magnitude of digit (i.e. 4) */
	rnum = pnum * fnum;         /* get specific digit (i.e. 1000 * 4) */
	num -= rnum;                /* subtract that digit from the full number */
	o_buff[pos++] = fnum + '0'; /* convert the single digit int to a char */
    }
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Converts 0 padded strings to an integer.
 *  @param[in] i_buff The input buffer containing the string to decode.
 *  @param[in] size The size of the input stirng.
 *  @return The number as an integer.
 */
////////////////////////////////////////////////////////////////////////////////
int stonum( char * i_buff, int len )
{
    ////////////////////
    // Variables
    ////////////////////
    int i;                         /* navigation variable */
    int sum = 0;                   /* sum to return */
    int num;                       /* the single digit of the place */
    int pnum = len - 1;            /* the power number */
    int multiplier;

    // convert to char at buff[i] and multiply by 10^(other end)
    for ( i = 0; i < len; i++ )
    {
	multiplier = my_pow(10, pnum);
	pnum--;
	num = i_buff&#91;i&#93; - '0';
	if ( num > 0 )
	    num = num * multiplier;
	sum += num;
    }
    return sum;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Converts a string formatted as a double to an actual double.
 *  @param[in] i_buff The string to convert.
 *  @return The string result as a double.
 */
////////////////////////////////////////////////////////////////////////////////
double stodoub( char * i_buff )
{
    ////////////////////
    // Variable
    ////////////////////
    int size = strlen( i_buff );   /* determine the size of the string */
    int ptr = 0;                   /* i_buff navigation variable  */
    double result = 0;             /* the resulting double */
    int i;                         /* power calculation variable  */

    // Read in the Integer
    while( ptr < size && i_buff&#91;ptr&#93; != '.' )
    {
	ptr++;
    }
    result += stonum( i_buff, ptr );
    ptr++;                         /* consume '.' */

    i = 1;
    while( ptr < size )
    {
	double pnum = (double) my_pow(10, i++);  /* determine power */
	double x = i_buff&#91;ptr++&#93; - '0';          /* char # to int (inc ptr) */
	result += x / pnum;                      /* decimal digit (inc i) */
    }
    return result;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Converts a double to a zero padded string.
 *  @param&#91;out&#93; o_buff The already allocated string to print the double into.
 *  @param&#91;in&#93; num The double to print into the string.
 *  @param&#91;in&#93; size The size of o_buff.
 *  @return void
 */
////////////////////////////////////////////////////////////////////////////////
/* int dtos( char * o_buff, double num ) */
/* { */
/*     //////////////////// */
/*     // Variables */
/*     //////////////////// */
/*     int size;                      /\* the size of the double to be stored *\/ */
/*     char t_buff&#91;200&#93;;              /\* temp storage to determine sizeof double *\/ */

/*     // determine size of conversion from double to string */
/*     //size = sprintf( t_buff, "%099.99lf", num ); */
/*     // malloc enough memory for the double as a string */
/*     /\* o_buff = (char *) malloc (sizeof(char) * size) ; *\/ */
/*     /\* if ( o_buff == NULL ) *\/ */
/*     /\* 	fprintf (stderr, "layer.h: : Failed to malloc() o_buff ") ; *\/ */
/*     return sprintf( o_buff, "%.99lf", num); */
/* } */
/* #endif /\* __UTILITY_H__ *\/ */

&#91;/c&#93;
<h2>layer.h</h2>
[c]
#ifndef LAYER_H
#define LAYER_H
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer.h
 *  @brief Global header file for all layers, containging all necessary
 *  libraries and defining global preprocessor directives such as the chunk
 *  size.
 *
 *  Additionally preprocessor directives are used to limit the scope of testing
 *  various layers. Set layer from 1-5 for testing and 0 for normal use.
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 */
///////////////////////////////////////////////////////////////////////////////
/**
 *
 * A chunk ::  a sequence of bytes whose length is no greater than 16 bytes
 * - Each of the bytes in a chunk can have any value, including binary data and
 *   the '\0' character; only restriction is there are no more than 16 bytes.
 * - Note that it is also valid to send/receive a chunk of size 0.
 *
 */
////////////////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------------- :Global:
//                            ___ _     _          _
//                           / __| |___| |__  __ _| |
//                          | (_ | / _ \ '_ \/ _` | |
//                           \___|_\___/_.__/\__,_|_|
//
// -----------------------------------------------------------------------------
#define MAX_CHUNK_SIZE 16          /* sets chunk for all layers as 16 bytes */
#define LAYER 0                    /* 1-5 for testing. 0 for normal use */
//------------------------------------------------------------------ :Libraries:
//                      _    _ _                 _
//                     | |  (_) |__ _ _ __ _ _ _(_)___ ___
//                     | |__| | '_ \ '_/ _` | '_| / -_|_-<
//                     |____|_|_.__/_| \__,_|_| |_\___/__/
//
// -----------------------------------------------------------------------------
#include <stdio.h>	           /* printf, fprintf, etc */
#include <unistd.h>   	           /* need this for read(), write() */
#include <stdlib.h>	           /* needed for exit()  */
#include <string.h>	           /* strlen, etc */
/* ------------------------------------------------------------------- :Student:
 *                      ___ _           _         _
 *                     / __| |_ _  _ __| |___ _ _| |_
 *                     \__ \  _| || / _` / -_) ' \  _|
 *                     |___/\__|\_,_\__,_\___|_||_\__|
 *
 * -------------------------------------------------------------------------- */
typedef struct
{
    char * firstname;
    char * lastname;
    int rin;
    double gpa;
} student;

extern void print_title( int num, FILE * mode, char* title );
extern int my_pow(int x, int n);
extern int bin_exp(int num);
extern void itozstring( char * o_buff, int num, int len );
extern int stonum( char * i_buff, int len );
extern double stodoub( char * i_buff );
////////////////////////////////////////////////////////////////////////////////
extern int layer1_read( char * b );
extern int layer1_write( char b );
extern int layer2_write( char * chunk, int len );
extern int layer2_read( char * chunk, int max );
extern int layer3_write( char * msg, int len );
extern int layer3_read( char * msg, int max );
extern int layer4_write( char * msg, int len );
extern int layer4_read( char * msg, int max );
extern int layer5_write( student * stu );
extern int layer5_read( student * stu );
//--------------------------------------------------------------------- :Layers:
//                          _
//                         | |   __ _ _  _ ___ _ _ ___
//                         | |__/ _` | || / -_) '_(_-<
//                         |____\__,_|\_, \___|_| /__/
//                                    |__/
// -----------------------------------------------------------------------------
////////////////////
// Layer 1
////////////////////
#if ( LAYER >= 1 )
#include "layer1.c"
#endif /* LAYER1 */
////////////////////
// Layer 2
////////////////////
#if ( LAYER >= 2 )
#include "layer2.c"
#endif /* LAYER2 */
////////////////////
// Layer 3
////////////////////
#if ( LAYER >= 3 )
#include "layer3.c"
#endif /* LAYER3 */
////////////////////
// Layer 4
////////////////////
#if ( LAYER >= 4 )
#include "layer4.c"
#endif /* LAYER4 */
////////////////////
// Layer 5
////////////////////
#if ( LAYER >= 5 )
#include "layer5.c"
#endif /* LAYER5 */

#endif /* LAYER_H */

layer1.c

#ifndef LAYER_1
#define LAYER_1
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer1.c
 *  @brief Layer 1 maintains byte ordering such that the receiver receives the
 *         first byte sent before receiving the second byte, etc.
 *
 *
 *  Sample layer 1 implementation - this can be used to provide half-duplex
 *  communication by using the shell to create a pipe between a sender process
 *  and a receiver process.
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -c layer1.c -o ../build/layer1.o
 */
////////////////////////////////////////////////////////////////////////////////
/**
 *
 * A chunk ::  a sequence of bytes whose length is no greater than 16 bytes
 * - Each of the bytes in a chunk can have any value, including binary data and
 *   the '\0' character; only restriction is there are no more than 16 bytes.
 * - Note that it is also valid to send/receive a chunk of size 0.
 *
 */
////////////////////////////////////////////////////////////////////////////////
/**
 *  TODO List
 *  - Add check function that checks max length vs actual length (const)
 */
////////////////////////////////////////////////////////////////////////////////
/* ----------------------------------------------------------------- :Libraries:
 *                    _    _ _                 _
 *                   | |  (_) |__ _ _ __ _ _ _(_)___ ___
 *                   | |__| | '_ \ '_/ _` | '_| / -_|_-<
 *                   |____|_|_.__/_| \__,_|_| |_\___/__/
 *
 * -------------------------------------------------------------------------- */
#include "layer.h"
//----------------------------------------------------------------- :Prototypes:
//                  ___         _       _
//                 | _ \_ _ ___| |_ ___| |_ _  _ _ __  ___ ___
//                 |  _/ '_/ _ \  _/ _ \  _| || | '_ \/ -_|_-<
//                 |_| |_| \___/\__\___/\__|\_, | .__/\___/__/
//                                          |__/|_|
//
//-----------------------------------------------------------------------------
int layer1_write(char b);
int layer1_read( char * b );
/* -------------------------------------------------------------------- :Layer 1:
 *                          _                        _
 *                         | |   __ _ _  _ ___ _ _  / |
 *                         | |__/ _` | || / -_) '_| | |
 *                         |____\__,_|\_, \___|_|   |_|
 *                                    |__/
 * -------------------------------------------------------------------------- */
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Sample layer1_write just calls write to stdout.
 *  @param&#91;in&#93; b
 *  @return The number of bytes written: 1 if everything goes well. -1 if there
 *          was an error.
 */
////////////////////////////////////////////////////////////////////////////////
int layer1_write( char b )
{
  if ( write( 1, &b, 1 ) != 1 )
  {
    return -1;
  }
  else
  {
    return 1;
  }
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Sample layer1_read just calls read on stdin.
 *  @param&#91;in&#93; b
 *  @return The number of bytes read: 1 if everything goes well. -1 if there was
 *          an error.
 */
////////////////////////////////////////////////////////////////////////////////
int layer1_read( char * b )
{
  if ( read( 0, b, 1 ) != 1 )
  {
    return -1;
  }
  else
  {
    return 1;
  }
}
#endif /* LAYER_1 */

&#91;/c&#93;

<h2>layer2.c</h2>
[c]
#ifndef LAYER2_H
#define LAYER2_H
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer2.c
 *  @brief Layer 2 provides the transmission and receipt of a chunk of data.
 *
 *  DETAILED DESCRIPTION
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -c layer4.c -o ../build/layer4.o
 *
 */
////////////////////////////////////////////////////////////////////////////////
/* ----------------------------------------------------------------- :Libraries:
 *                    _    _ _                 _
 *                   | |  (_) |__ _ _ __ _ _ _(_)___ ___
 *                   | |__| | '_ \ '_/ _` | '_| / -_|_-<
 *                   |____|_|_.__/_| \__,_|_| |_\___/__/
 *
 * -------------------------------------------------------------------------- */
#include "layer.h"
/*--------------------------------------------------------------------- :Global:
 *                            ___ _     _          _
 *                           / __| |___| |__  __ _| |
 *                          | (_ | / _ \ '_ \/ _` | |
 *                           \___|_\___/_.__/\__,_|_|
 *
 * -------------------------------------------------------------------------- */
#define SCHKSZE 2       /* small check size */
// -------------------------------------------------------------------- :Layer 2:
//                         _                        ___
//                        | |   __ _ _  _ ___ _ _  |_  )
//                        | |__/ _` | || / -_) '_|  / /
//                        |____\__,_|\_, \___|_|   /___|
//                                   |__/
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Sends a chunk that consists of the sequence of bytes starting at the
 *         address specified by the first parameter (chunk) with length len.
 *  @warning all errors that can be detected here must be handled here including
 *           valid values of len and the return value of layer1_write()
 *  @param&#91;in&#93; chunk The data to write using layer 1 protocols.
 *  @param&#91;in&#93; len The size of the chunk to write.
 *  @return len on success, -1 on error
 */
////////////////////////////////////////////////////////////////////////////////
int layer2_write( char * chunk, int len )
{
    ////////////////////
    // Variables
    ////////////////////
    int i;                         /* navigation variable */
    char size_buffer&#91;SCHKSZE&#93;;     /* used to send chunk size to layer2_read */

    // return error if length is greater than max chunk size
    if ( len > MAX_CHUNK_SIZE )
    {
	return -1;
    }
    // send size of chunk to be sent
    itozstring( size_buffer, len, SCHKSZE ); //unable to use globals with sprintf
    for ( i = 0; i < SCHKSZE; i++ )
    {
	layer1_write( size_buffer&#91;i&#93; );
    }
    // write chunk using layer 1 for each char while checking errors
    for ( i = 0; i < len; i++ )
    {
    	if( layer1_write( chunk&#91;i&#93; ) == -1 )
    	{
    	    return -1;
    	}
    }
    return len;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Reads a chunk and stores the incoming bytes in the buffer starting at
 *         the address specified by the first parameter (chunk).
 *
 *  @warning Make sure that your layer2_read() does not allow the sender to
 *           overflow the buffer! And it's not enough to recognize when this has
 *           happened and return an error; you must not store anything beyond
 *           the max location of chunk.
 *  @param&#91;in&#93; chunk A sequence of bytes whose length is no greater than 16
 *             bytes as defined by MAX_CHUNK_SIZE which can contain any type of
 *             data.
 *  @param&#91;in&#93; max No more than max bytes will be put into chunk, so max limits
 *             the size of the chunk read.
 *  @return Upon successfully receiving a chunk, the size of the chunk in bytes
 *          is returned.
 */
////////////////////////////////////////////////////////////////////////////////
int layer2_read( char * chunk, int max )
{
    ////////////////////
    // Variables
    ////////////////////
    int msg_size = 0;              /* stores the size of the message */
    int i;                         /* counter variable for for loops */
    char c;                        /* used for reading characters from layer 1 */
    char size_buffer&#91;SCHKSZE&#93;;     /* used for storing size of string */
    // receive the size of msg and convert to int
    for (i = 0; i < SCHKSZE; i++)
    {
	if ( layer1_read( &c ) == -1 )
	{
	    return -1;
	}
	size_buffer&#91;i&#93; = c;
    }
    msg_size = stonum( size_buffer, SCHKSZE );
    /*
     * Error Checking - confirm chunk !> than allocated memory or MAX_CHUNK_SIZE
     * - IF a chunk is received by layer2_read() that would require more than
     *	 max bytes, layer2_read() returns -1 (error).
     */
    if ( msg_size > max )
    {
	fprintf(stderr, "LAYER 2 ERROR -  Message Size of %d is too big\n", msg_size);
    	return -1;
    }
    // read in data using layer 1
    for ( i = 0; i < msg_size; i++ )
    {
    	if( layer1_read( &c ) == -1 )
    	{
    	    return -1;
    	}
    	chunk&#91;i&#93; = c;
    }
    return msg_size;
}
#endif /* LAYER2_H */

&#91;/c&#93;

<h2>layer3.c</h2>
[c]
#ifndef LAYER3_H
#define LAYER3_H
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer3.c
 *  @brief Layer 3 provides the capability to send and receive messages, where a
 *  message is defined as any sequence of bytes.
 *
 *  More specifically, there is no length limitation at layer 3, so a sender can
 *  ask layer 3 to send any size message.
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -c layer3.c -o ../build/layer3.o
 */
////////////////////////////////////////////////////////////////////////////////
/* ----------------------------------------------------------------- :Libraries:
 *                    _    _ _                 _
 *                   | |  (_) |__ _ _ __ _ _ _(_)___ ___
 *                   | |__| | '_ \ '_/ _` | '_| / -_|_-<
 *                   |____|_|_.__/_| \__,_|_| |_\___/__/
 *
 * -------------------------------------------------------------------------- */
#include "layer.h"
/* -------------------------------------------------------------------- :Layer 3:
 *                         _                        ____
 *                        | |   __ _ _  _ ___ _ _  |__ /
 *                        | |__/ _` | || / -_) '_|  |_	\
 *                        |____\__,_|\_, \___|_|   |___/
 *                                   |__/
 * -------------------------------------------------------------------------- */
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Sends a message.
 *  @param&#91;in&#93; msg A message of any length to send.
 *  @param&#91;in&#93; len The size of the message to send.
 *  @return The number of bytes (of the message) sent on success (should be len)
 *          or -1 on error.
 */
////////////////////////////////////////////////////////////////////////////////
int layer3_write( char * msg, int len )
{
    ////////////////////
    // Variables
    ////////////////////
    int counter = 0;               /* keeps track of bytes sent */

    // write message to layer 2
    // should never write an empty message in this loop, since len would be 0
    while( len > 0 )
    {
	// when remaing msg is greater than chunk, just send whats left
	if( len < MAX_CHUNK_SIZE )
	{
	    if( layer2_write( &msg&#91;counter&#93;, len) == -1 )
	    {
		return -1;
	    }
	    counter += len;
	    len = 0;
	}
	else
	{
	    // when the remaing msg is less than chunk, send a full chunk
	    if( layer2_write( &msg&#91;counter&#93;, MAX_CHUNK_SIZE ) == -1 )
	    {
		return -1;
	    }
	    counter += MAX_CHUNK_SIZE;
	    len -= MAX_CHUNK_SIZE;
	}
    }
    // always write an empty message to signify end
    char foo;
    layer2_write(&foo, 0);
    return counter;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief reads a message and stores it in memory starting at the address
 *         specified by msg
 *  @param&#91;in&#93; msg Where the message that is read is stored.
 *  @param&#91;in&#93; max No more than max bytes will be put into memory, so max must
 *             limit the size of the message read.
 *  @return The size of the message received or -1 on error.
 *
 * If a message is received by layer3_read() that would require more than max
 * bytes, layer3_read() must return -1 (indicating an error).
 */
////////////////////////////////////////////////////////////////////////////////
int layer3_read( char * msg, int max )
{
    ////////////////////
    // Variables
    ////////////////////
    int counter = 0;               /* keeps track of position in msg */
    char t_buff&#91;MAX_CHUNK_SIZE&#93;;   /* a temporary buffer to prevent mem overlow */
    int chars_read = 0;            /* chars read from a single layer 2 read */
    int total_chars_read = 0;      /* total characters read using layer 2 */
    int i;                         /* for loop navigation variable */

    do
    {
	chars_read = 0;            /* reset counter */
	// read the next chunk of characters into temporary chunk
	chars_read = layer2_read( t_buff, MAX_CHUNK_SIZE );
	if( chars_read == -1 )
	{
	    fprintf(stderr, "LAYER 3 ERROR: layer2_read Failed.\n");
	    return -1;
	}
	total_chars_read += chars_read;
	// check to make sure there is no character overflow
	if( total_chars_read > max )
	{
	    fprintf( stderr, "LAYER 3 ERROR: memory allocation exceded\n" );
	    return -1;
	}
	// store the temp buffer characters in the real buffer
	for ( i = 0; i < chars_read; i++ )
	{
	    msg&#91;counter++&#93; = t_buff&#91;i&#93;;
	}
    } while( chars_read > 0 );
    return total_chars_read;
}
#endif /* LAYER3_H */

layer4.c

#ifndef LAYER4_H
#define LAYER4_H
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer4.c
 *  @brief Layer 4 adds simple error detection to layer 4 services
 *
 *  The errors to look for here involve transmission errors; we want to somehow
 *   make sure that the message received is the same as the message that was
 *   sent.  The simplest approach to error detection is to use a checksum.  The
 *   checksum is the sum % 65536.  The resulting two-byte checksum should be
 *   sent along with the message data; the receiving end goes through the same
 *   steps to compute the checksum, then compares the received checksum to the
 *   computed checksum.
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -c layer4.c -o ../build/layer4.o
 */
////////////////////////////////////////////////////////////////////////////////
#include "layer.h"
////////////////////////////////////////////////////////////////////////////////
/* -------------------------------------------------------------------- :Global:
 *                            ___ _     _          _
 *                           / __| |___| |__  __ _| |
 *                          | (_ | / _ \ '_ \/ _` | |
 *                           \___|_\___/_.__/\__,_|_|
 *
 * -------------------------------------------------------------------------- */
#define CHKFRSZ 4
/* -------------------------------------------------------------------- :Layer 4:
 *                        _                        _ _
 *                       | |   __ _ _  _ ___ _ _  | | |
 *                       | |__/ _` | || / -_) '_| |_  _|
 *                       |____\__,_|\_, \___|_|     |_|
 *                                  |__/
 * -------------------------------------------------------------------------- */
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Computes a checksum of the message and sends it to layer4_read.
 *  @param[in] msg The message to send.
 *  @param[in] len The length of the message.
 *  @return The number of Bytes sent.
 */
////////////////////////////////////////////////////////////////////////////////
int layer4_write( char * msg, int len )
{
    ////////////////////
    // Variables
    ////////////////////
    int checksum = 0;              /* checksum to calculate and send */
    char chksum_buff[CHKFRSZ];     /* store checksum as string */
    int i = 0;                     /* navigator variable */

    // Compute Checksum
    for (i = 0; i < len; i++)
    {
	checksum += msg&#91;i&#93;;
    }
    checksum = checksum % 65536;
    itozstring( chksum_buff, checksum, CHKFRSZ ); /* convert int to string */
    /* sprintf(size_buffer, "%04d", checksum); */
    // Write checksum to layer 3 and check for errors
    if( layer3_write( chksum_buff, CHKFRSZ ) == -1 )
    {
    	return -1;
    }
    // Send Message
    if( layer3_write( msg, len ) == -1 )
    {
    	return -1;
    }
    // Return the number of bytes sent on success
    return len;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Reads a message into memory starting at the address specified by msg.
 *  @param&#91;in&#93; msg No more than max bytes will be put into memory, so max must
 *             limit the size of the message read.
 *  @param&#91;in&#93; max The maximum size of the message.
 *  @return -1 if max size is incorrect or if there is a transmission error.
 *          The size of the message in bytes is returned otherwise.
 */
////////////////////////////////////////////////////////////////////////////////
int layer4_read( char * msg, int max )
{
    ////////////////////
    // Variables
    ////////////////////
    int sent_chksum = 0;           /* checksum recieved */
    int calc_chksum = 0;           /* checksum calculated */
    char chksum_buff&#91;CHKFRSZ&#93;;     /* store checksum as string */
    int msg_size = 0;              /* the size of the message recieved */
    int i = 0;                     /* for loop navigation variable */

    // Read in the Checksum
    if( layer3_read( chksum_buff, CHKFRSZ ) == -1 )
    {
	fprintf( stderr, "LAYER 4 ERROR: layer3_read Failed.\n" );
    	return -1;
    }
    sent_chksum = stonum( chksum_buff, CHKFRSZ );
    // Read in Input and Check for Errors
    /* If a message is received by layer4_read() that would require more than
     * bytes, layer4_read() must return -1 (error). */
    if( (msg_size = layer3_read( msg, max )) == -1 )
    {
	fprintf( stderr, "LAYER 4 ERROR: layer3_read Failed.\n" );
    	return -1;
    }
    // Check Checksum
    /* If your error-detection mechanism detects transmission error(s),
     * layer4_read() must return a -1. */
    calc_chksum = 0;
    for ( i = 0; i < msg_size; i++ )
    {
    	calc_chksum += (int) msg&#91;i&#93;;
    }
    // Compare Calcualted Checksum to Sent Checksum
    if( (calc_chksum % 65536) != sent_chksum )
    {
	fprintf( stderr, "LAYER 4 ERROR: checksum match failed .\n" );
    	return -1;
    }
    return msg_size;
}
#endif /* LAYER4_H */

&#91;/c&#93;

<h2>layer5.c</h2>
[c]
#ifndef LAYER5_H
#define LAYER5_H
////////////////////////////////////////////////////////////////////////////////
/**
 *  @file layer5.c
 *  @brief Layer 5 provides higher application layers with a mechanism for
 *  sending and receiving a simple C struct.
 * 
 *  The sending function (i.e. layer5_write()) is given the address of the
 *  struct to be sent, and the corresponding call to layer5_read() in the peer
 *  will dynamically create an identical struct. Both peer processes have the
 *  definition of the struct ahead of time (compiled in), and the functions are
 *  written with this knowledge. Note that some languages (e.g. Java) have a
 *  mechanism to ensure the structures on each side match up (i.e. are the same
 *  "version" of code); we'll explore this in the future.
 *
 *  @title Peer-to-peer and Interface Protocols Using C - Layer 5
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 * 
 *  Compile with: gcc -c layer5.c -o ../build/layer5.o
 */
////////////////////////////////////////////////////////////////////////////////
/* ----------------------------------------------------------------- :Libraries:
 *                    _    _ _                 _        
 *                   | |  (_) |__ _ _ __ _ _ _(_)___ ___
 *                   | |__| | '_ \ '_/ _` | '_| / -_|_-<
 *                   |____|_|_.__/_| \__,_|_| |_\___/__/
 *                                                      
 * -------------------------------------------------------------------------- */
#include "layer.h"
/* -------------------------------------------------------------------- :Global:
 *                            ___ _     _          _ 
 *                           / __| |___| |__  __ _| |
 *                          | (_ | / _ \ '_ \/ _` | |
 *                           \___|_\___/_.__/\__,_|_|
 *                                                   
 * -------------------------------------------------------------------------- */
#define TSIZE 2
#define RINSIZE 15                 /* Bytes stored in RIN */
/* -------------------------------------------------------------------- :Layer 5:
 *                         _                        ___ 
 *                        | |   __ _ _  _ ___ _ _  | __|
 *                        | |__/ _` | || / -_) '_| |__	\
 *                        |____\__,_|\_, \___|_|   |___/
 *                                   |__/               
 * -------------------------------------------------------------------------- */
////////////////////////////////////////////////////////////////////////////////
/*
 *  @brief The layer5_read() function must use layer4_read() to read a student
 *         and put the received field values (firstname, lastname, rin, and gpa)
 *         into the student pointed to by stu.
 *  @warning You may assume that stu points to memory that has been allocated
 *           and is large enough to store the two pointers, the int, and the
 *           double.
 *  @param&#91;in&#93; stu The student struct to write.
 *  @return The return value is 1 on success, or -1 on error. 
 */
////////////////////////////////////////////////////////////////////////////////
int layer5_write( student * stu )
{
    ////////////////////
    // Variables
    ////////////////////
    //@TODO Sloppy to use 200 as size
    char output&#91;200&#93;;              /* huge buffer to store struct stream inpu */
    int name_size;                 /* size of first or last name */
    int o_ptr = 0;                 /* write location in output buff */

    // First Name
    name_size = (int)strlen( stu->firstname );
    // first name size error handling
    if( name_size > 81 )
    {
	fprintf( stderr, "LAYER 5 ERROR: First Name exceeds 80 characters.\n");
	return -1;
    }
    itozstring( output, name_size, TSIZE );
    o_ptr += 2;
    o_ptr += sprintf( &output[o_ptr], "%s", stu->firstname );
    // Last Name
    name_size = (int)strlen( stu->lastname );
    // last name size error handling
    if( name_size > 81 )
    {
	fprintf( stderr, "LAYER 5 ERROR: Last Name exceeds 80 characters.\n" );
	return -1;
    }
    itozstring( &output[o_ptr], name_size, TSIZE );
    o_ptr += 2;
    o_ptr += sprintf( &output[o_ptr], "%s", stu->lastname );
    // RIN
    itozstring( &output[o_ptr], stu->rin, RINSIZE );
    o_ptr += RINSIZE;
    // GPA
    o_ptr += sprintf( &output[o_ptr], "%016lf", stu->gpa );
    // Send Output through layer 4
    layer4_write( output, o_ptr );
    
    return 1;
}
////////////////////////////////////////////////////////////////////////////////
/**
 *  @brief Dynamically allocate memory of the appropriate size for the firstname
 *         and lastname fields.
 *  @warning It is the responsibility of the caller to free this memory.
 *  @param[out] stu The student struct to read onto.
 *  @return The return value is 1 on success, or -1 on error. 
 */
////////////////////////////////////////////////////////////////////////////////
int layer5_read( student * stu )
{
    ////////////////////
    // Variables
    ////////////////////
    int name_size;
    char input[1000];
    int i_ptr = 0;
    int i;

    // Read in layer 4
    if( layer4_read(input, 1000 ) == -1 )
    {
	return -1;
    }
    ////////////////////////////////////////
    // Store Data in Student
    ////////////////////////////////////////
    // First Name
    name_size = stonum( input, 2 );
    stu->firstname = (char *) malloc ( sizeof(char) * name_size ) ;
    // allocate memory for first name
    if ( stu->firstname == NULL )
	fprintf ( stderr, "layer5.c: : Failed to malloc() stu->firstname " ) ;
    i_ptr += 2;                    /* incrament to consume size string */
    // store the first name from input into actual structure
    for ( i = 0; i < name_size; i++ )
    {
	stu->firstname[i] = input[i_ptr++];
    }
    stu->firstname[i] = '\0';      /* make sure the string is null terminated */
    // Last Name
    name_size = stonum( &input[i_ptr], 2 );
    stu->lastname = (char *) malloc ( sizeof(char) * name_size ) ;
    if ( stu->lastname == NULL )
	fprintf ( stderr, "layer5.c: : Failed to malloc() stu->lastname " ) ;
    i_ptr += 2;                    /* incrament to consume size string */
    // store the last name from input into actual structure
    for ( i = 0; i < name_size; i++ )
    {
	stu->lastname[i] = input[i_ptr++];
    }
    stu->lastname[i] = '\0';      /* make sure the string is null terminated */
    // RIN
    stu->rin = stonum( &input[i_ptr], RINSIZE );
    i_ptr += RINSIZE;
    // GPA
    stu->gpa = stodoub( &input[i_ptr] );
    return 1;
}
#endif /* LAYER5_H */

client.c




////////////////////////////////////////////////////////////////////////////////
/**
 *  @file client.c
 *  @brief Used to test layer.h
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -Wall -o ../bin/client layer.c client.c;
 *                gcc -Wall -o ../bin/server layer. server.c;
 *     Test with: ./client | ./server; cat out.list; rm out.list;
 */
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------ :Libraries:
//                      _    _ _                 _
//                     | |  (_) |__ _ _ __ _ _ _(_)___ ___
//                     | |__| | '_ \ '_/ _` | '_| / -_|_-<
//                     |____|_|_.__/_| \__,_|_| |_\___/__/
//
// -----------------------------------------------------------------------------
#include "layer.h"
/*----------------------------------------------------------------------- :Main:
 *                              __  __      _
 *                             |  \/  |__ _(_)_ _
 *                             | |\/| / _` | | ' \
 *                             |_|  |_\__,_|_|_||_|
 *
 -----------------------------------------------------------------------------*/
int main ( int argc, char* argv[] )
{
    //////////////////////////////
    // Layer 1 Write Testing
    //////////////////////////////
#if (LAYER == 1)
    // Write
    char f = 'l';
    layer1_write(f);
#endif /* TEST1 */
#if (LAYER == 2)
    //////////////////////////////
    // Layer 2 Write Testing
    //////////////////////////////
    // Basic Test Case
    layer2_write("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 4);
#endif /* TEST2 */
#if (LAYER == 3)
    //////////////////////////////
    // Layer 3 Write Testing
    //////////////////////////////
    //layer3_write("aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbcccccccc", 40);
    layer3_write("aaaaaaaaaaaaaaaaaaaaaaaaa", 25);
#endif /* TEST3 */
#if (LAYER == 4)
    //////////////////////////////
    // Layer 4 Write Testing
    //////////////////////////////
    layer4_write("aaaaaaaaaaaaaaaaaaaaaaaaa", 25);
#endif /* TEST4 */
#if (LAYER == 5)
    //////////////////////////////
    // Layer 5 Write Testing
    //////////////////////////////
    student my_student;
    // Store Data in Structure
    my_student.firstname = "Nick";
    my_student.lastname = "Guthrie";
    my_student.rin = 660451925;
    my_student.gpa = 3.95;
    // Test Write
    layer5_write( &my_student );
#endif /* TEST5 */
    return 0;
}


[/c]

server.c




////////////////////////////////////////////////////////////////////////////////
/**
 *  @file server.c
 *  @brief Server Side used to test layer.h.
 *
 *  @title Peer-to-peer and Interface Protocols Using C
 *  @author Nicholas Guthrie
 *  @email guthrn@rpi.edu
 *  @web http//nickguthrie.com
 *  @created January 30, 2014
 *
 *  Compile with: gcc -Wall -o ../bin/client client.c;
 *                gcc -Wall -o ./server server.c
 *     Test with: ./client | ./server; cat out.list; rm out.list;
 */
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------ :Libraries:
//                      _    _ _                 _
//                     | |  (_) |__ _ _ __ _ _ _(_)___ ___
//                     | |__| | '_ \ '_/ _` | '_| / -_|_-<
//                     |____|_|_.__/_| \__,_|_| |_\___/__/
//
// -----------------------------------------------------------------------------
#include "layer.h"
/*----------------------------------------------------------------------- :Main:
 *                              __  __      _
 *                             |  \/  |__ _(_)_ _
 *                             | |\/| / _` | | ' \
 *                             |_|  |_\__,_|_|_||_|
 *
 * ---------------------------------------------------------------------------*/
int main ( int argc, char* argv[] )
{
    FILE *ofp;
    char outputFilename[] = "out.list";
    ofp = fopen( outputFilename, "w" );
    if ( ofp == NULL)
    {
	fprintf( stderr, "Can't open output file %s!\n", outputFilename );
	exit(1);
    }
#if (LAYER==1)
    //////////////////////////////
    // Layer 1 Read Testing
    //////////////////////////////
    print_title(0, ofp, "LAYER 1 Testing");
    char b;
    layer1_read(&b);
    fprintf(ofp, "%c", b);
    print_title(1, ofp, "LAYER 1 Testing");
#endif /* TEST1 */
#if(LAYER == 2)
    //////////////////////////////
    // Layer 2 Read Testing
    //////////////////////////////
    print_title(0, ofp, "LAYER 2 Testing");
    ////////////////////
    // Variables
    ////////////////////
    char chunk[5];                 /* the read chunk here */
    int i;                         /* navigation variable */
    int chunk_size_read;           /* size of chunk read */
    ////////////////////
    // Test Read
    ////////////////////
    chunk_size_read = layer2_read( chunk, 5 );
    ////////////////////
    // Print Results
    ////////////////////
    for ( i = 0; i < chunk_size_read; i++ )
    {
    	fprintf(ofp, "%c", chunk[i]);
    }
    print_title(1, ofp, "LAYER 2 Testing");
#endif /* TEST2 */
#if(LAYER == 3)
    //////////////////////////////
    // Layer 3 Read Testing
    //////////////////////////////
    print_title( 0, ofp, "LAYER 3 Testing" );
    ////////////////////
    // Variables
    ////////////////////
    char * chunk;
    int i;
    int msg_size;
    int chunk_size = 10;
    // Allocate Memory for Chunk
    chunk = (char *) malloc (sizeof(char) * chunk_size) ;
    if (chunk == NULL)
	return -1;
    // Test Read Function
    msg_size = layer3_read( chunk, chunk_size );
    // Print Results to File
    for ( i = 0; i < msg_size; i++ )
    {
    	fprintf(ofp, "%c", chunk[i]);
    }
    print_title(1, ofp, "LAYER 3 Testing");
#endif /* TEST3 */
#if(LAYER == 4)
    //////////////////////////////
    // Layer 4 Read Testing
    //////////////////////////////
    print_title(0, ofp, "LAYER 4 Testing");
    ////////////////////
    // Variables
    ////////////////////
    char * chunk;
    int chunk_size = 100;
    int i;
    // Allocate Memory for Chunk
    chunk = (char *) malloc (sizeof(char) * chunk_size) ;
    if (chunk == NULL)
	return -1;
    // Test Read Function
    layer4_read( chunk, chunk_size );
    // Print Results to File
    for ( i = 0; i < chunk_size; i++ )
    {
    	fprintf(ofp, "%c", chunk[i]);
    }
    print_title(1, ofp, "LAYER 4 Testing");
#endif /* TEST4 */
#if(LAYER == 5)
    //////////////////////////////
    // Layer 5 Read Testing
    //////////////////////////////
    print_title(0, ofp, "LAYER 5 Testing");
    ////////////////////
    // Variables
    ////////////////////
    student my_student;
    // Allocate Student Memory
    my_student.firstname = (char *) malloc ( sizeof(char) * 80 ) ;
    if ( my_student.firstname == NULL )
	fprintf ( stderr, "server.c: : Failed to malloc() my_student.firstname " ) ;
    my_student.lastname = (char *) malloc ( sizeof(char) * 80 ) ;
    if ( my_student.lastname == NULL )
	fprintf ( stderr, "server.c: : Failed to malloc() my_student.lastname " ) ;
    // Test Read Function
    layer5_read( &my_student );
    // Print Results
    printf("FIRST NAME:  %s\n", my_student.firstname);
    printf(" LAST NAME:  %s\n", my_student.lastname);
    printf("       RIN:  %d\n", my_student.rin);
    printf("       GPA:  %.2lf\n", my_student.gpa);
    print_title(1, ofp, "LAYER 5 Testing");
#endif /* TEST5 */
    return 0;
}


[/c]


 

         
A file storage server implemented in C

Categorized Under

CLanguagesProgramsSchoolwork

About Nick Guthrie

» has written 38 posts

History