CpSc 3600-001 Homework 1: Layered Communication Software

$30.00

Category: Tags: , , , , You will Instantly receive a download link for .zip solution file upon Payment || To Order Original Work Click Custom Order?

Description

5/5 - (2 votes)

Developing a C program to support half-duplex layered communication. You are provided with the interface protocols (C function prototypes) for all the layers.  You need to implement these layers, including the development of peer-to-peer protocols.  You are responsible for implementing the C functions in layers 2,3, 4 and 5 (described below).  You do not need to implement layer 1 code – I’ve included a sample layer 1 code along with a sample main program.  When testing your submission we will use our own layer 1 that may be very different from the one included here – make sure your code does not depend on any specific layer 1 implementation!

Sample code for l1 is in the fold


Layer 1: read/write a single byte:

Layer 1 interface protocol is specified here, but you don’t need to write this layer – just use it. A sample implementation is provided that will support testing of your code – the sample layer will support half-duplex communication using a pipe.

In layer 1 APIs, each function returns an int that indicates the number of bytes read/written, in this case the return value should be 1 if it is successful, -1 indicates errors.

int l1_write(char b);

Function:  Writes the byte specified by b.
Returns:    1 on success; -1 on error.

int l1_read(char *b);

Function:  Reads one byte and copies the byte to the address specified by b.
Returns:    1 on success, -1 on error.

IMPORTANT: Layer 1 does maintain ordering, so that the receiver will receive the first byte sent, before the second byte sent. You can (and should) assume that any layer 1 implementation maintains the order of individual bytes.


Layer 2: read/write a chunk

Layer 2 provides transmission/reception of a chunk. A chunk is defined as a sequence of bytes whose length is not longer than 10 bytes. Each of the bytes in a chunk can have any value, the only restriction is that there are no more than 10 bytes. It is valid to send/receive a chunk of size 0!

The important issue in layer 2 is that there must be some agreement (protocol) between the sender and the receiver as to what constitutes the chunk (how long is the chunk?  where is the end?). You must come up with a peer-to-peer protocol and implement the protocol here. The only way for l2_write to send anything is by using l1_write, and the only way l2_read can retrieve a byte from the sender is by calling l1_read.

The return value of each layer 2 function indicates the length of the chunk sent or received (in bytes) upon success, or the value -1 to indicate failure. This return value should reflect the number of bytes in the original chunk, it doesn’t count any extra bytes (header) your layer 2 might use as part of the peer-to-peer protocol.

IMPORTANT: The only assumption you can make about chunks is that the length is no longer than 10 bytes. The content of a chunk can be anything, the individual bytes can be anything in a chunk.

 

int l2_write(char *chunk, int len);

Function:  Sends a chunk starting at the address specified by the first parameter (chunk) and having length len.
Returns:  Returns 
len on success, -1 means an error occurred.
Note:  You need to handle all errors that can be detected here, this means you need to check for valid values of 
len, and that you check the return value each time you call l1_write.

int l2_read(char *chunk, int max);

Function:  Reads a chunk and stores the incoming byes starting at the address specified by the pointer chunk. No more than max bytes will be put in to memory, so max limits the size of the chunk read.
Returns:  If a chunk is received by 
l2_read that would require more than max bytes, -1 is returned (error). Upon successful reception of a chunk, the size of the chunk is returned.
Note:  Make sure that your 
l2_read does not allow the sender to overflow the buffer chunk!


Layer 3: read/write a message.

Layer 3 provides the capability to send and receive messages, where a message is defined as any sequence of bytes. There is no length limitation at layer 3, so a sender can ask layer 3 to send any size message. The general idea is that your layer 3 implementation needs to use layer 2 to send and receive small chunks, perhaps many times in order to transmit/receive a complete message.

The main issue at layer 3 is creation of a peer-to-peer protocol that uses chunks to communicate, and the receiver needs to know when it has reached the end of a message. Your l3_write can only call l2_write to send data (not l1_write), and your l3_read can only call l2_read.

int l3_write(char *msg, int len);

Function:  Sends a message of length len. The bytes sent are specified by the pointer msg.
Returns:  The number of bytes of sent on success (should be len), -1 means an error.

int l3_read(char *msg, int max);

Function:  Reads a message and stores in memory starting at the address specified by the pointer msg. No more than max bytes will be put in to memory, so max must limit the size of the message read no matter how large the message was sent by l3_write.
Returns:  Return value is the size of the message received or -1 on error. If a message is received by 
l3_read that would require more than max bytes, l3_read should return -1 (error).


Layer 4: read/write a message with error detection

Layer 4 adds simple error detection to the services provided by layer 3. The function prototypes (which define the interface protocols) looks the same as the layer 3 prototypes, the only difference is that the layer 4 read function should return a -1 (error) if it detects an error in the received message. The errors we are looking for here involve transmission errors, we want to try to make sure that the message received is the same as the message that was sent.

Error Detection: The simplest approach is to use a checksum. To use a checksum you simply add together all the bytes of the original message (just treat each byte as a number) and the checksum is the sum modulo 256 (to fit in a single byte). The result is a single byte that can be sent along with the message data, and the receiving end can go through the same steps (computing the checksum) and then compare the received checksum to the computed checksum. If they don’t agree – there was an error. If the checksums do agree, it is likely there was no error (but it’s not a certainty). Many network protocols use checksums to detect errors, although in general they use checksums larger than a single byte to improve the accuracy of the error detection. Feel free to use whatever you want to detect errors, a single byte checksum is just the easiest to implement. When testing your code we will use a buggy layer 1 that does introduce errors, so make sure your error detection works!

int l4_write(char *msg, int len);

Function:  Sends a message of length len. The bytes sent are specified by the pointer msg.
Returns:  Returns the number of bytes of the message sent on success (should be len), -1 means an error.

int l4_read(char *msg, int max);

Function:  Reads a message in to memory starting at the address specified by the pointer msg. No more than max bytes will be put in to memory, so max must limit the size of the message read.
Returns:  If a message is received by 
l4_read that would require more than max bytes, l4_read should return -1 (error). Some error detection mechanism is used to detect transmission errors. If such an error is detected by l4_read, a -1 will be returned. Upon successful reception of a message, the size of the message (the number of bytes stored in msg) is returned.


Layer 5: read/write a named value

Layer 5 will provide higher layers with a mechanism for sending and receiving values that have an associated name. The idea is that we could build an application-level protocol that uses layer 5 to send named values between peer processes. For example, a telephone directory service could be built by having clients send a request that could be either a person’s name, or phone number; the server could do the appropriate lookup and send back the appropriate result (as a named value).

Your layer 5 implementation should use layer 4 for sending and receiving messages (nothing else!). Once again, you need to come up with a peer-to-peer protocol so that your layer 5 functions know what to expect and can work together. It doesn’t matter what your peer-to-peer protocol is, as long as your l5_write works with your l5_read.

int l5_write(char *name, int namelen, char *value, int valuelen);

Function:  Sends the named value to the receiver.
Returns:  Returns a 1 on success, -1 means an error.

int l5_read(char *name, int *namelenptr, char *value, int *valuelenptr);

Function:  l5_read uses l4_read to read a named value and on success returns the value 1, and puts the name of the received value at name and the value at value. The pointers namelenptr and valuelenptr are pointers to the sizes of the two buffers when l5_read is initially called, you must use these values to make sure you don’t overflow the buffers.
Returns:  Upon successful return, 
*namelenptr should indicate the number of bytes in the name received and *valuelenptr should indicate the number of bytes in the value received. l5_read must return a -1 on error.


Sample Code and Testing

The program hw1test.c can be used to test your layer 5 code (which should use your layer 4 code, which, in turn, should use your layer 3 code, … ).

IMPORTANT: Although the sample code only accesses the layer 5 functions directly, it is necessary that your layers are independent and can work with any other implementation of other layers.  For example, we might use a different layer 2 implementation with your layer 3 and 4 code – everything should work correctly! Also keep in mind that when we test your code we will generate error conditions (including introducing some simulated transmission errors by providing a buggy layer 1).

The test code might be used as part of an application that uses layer 5 to send and receive a name/value pair where both the name and value are C strings (null terminated sequences of bytes). Don’t assume that layer 5 will always be used this way, your layer 5 should not care whether or not there is a terminating null!

The main program calls layer 5 to send or receive these messages depending on whether it finds command line arguments present. If there are 2 command line arguments the program assumes it is a sender and sends argv[1] as a name and argv[2] as the value. If no command line arguments are detected, the program tries to read a name value pair using l5_read. If everything works, the reader program will print the name and value to STDOUT.  Using the sample layer 5 code, you can run the program as a sender and pipe the output of the program to another copy of the program running as a receiver:

 

> ./hw1test phone 2766722 | ./hw1test
Name: phone
Value: 2766722

There is also a test program that can be used to test layer 3 directly (and layer 2 indirectly). This might give you more ideas about how to test each layer (don’t rely on the hw1test.c program alone!). Both of the test programs and a sample Makefile are available in sample code


Deliverables

You must provide us with one file for each of the 4 layers you will write. The layer 2 code must be in a file named l2.c, the layer 3 code in l3.c, and so on. Note that each of these files should have both a read and write function (l2.c should have l2_read() and l2_write(), …).

You do not need to provide any code for layer1 or a main program, we will supply our own when testing your code.


General submission requirements

For all the source code files, please include a header like the following,

/*

Author: clemson user name, Your Name(optional)

Filename: l2.c

Description: This file provides the implementation of Layer 2.

*/

Most IDEs should have the ability to let you do that conviniently using a template.

Do not put any unused file/ project file / binary file in the compessed package.

You must also include in your submission a file named README that includes your name and a brief description of your submission. It should have five clearly seperated sections titled with FILE DESCRIPTIONS, KNOWN PROBLEMS, DESIGN, SPECIAL BUILDING INSTRUCTIONS, and OTHER INFORMATION. In the section of FILE DESCRIPTIONS, you may record any information that is not provided in the hearder of source files, like any test input file, if there was any. In the section of KNOW PROBLEMS, write down all the problems or limitations of the program that you know of. Make sure that you understand this will not make your grade worse. Instead, knowing the problem and maybe why is much better than believing your buggy program can run correctly without testing. Please also indicate clearly which part of the submission do not work correctly to which extent, like “can not comile”, or “only producing wrong output for certain inputs”. So, test your program comprehensively before your submission with all the special cases that you can think of. In the section of DESIGN, you can write, if any, why you design your program that way. For the section of SPECIAL BUILDING INSTRUCTIONS, you may record any special enviromental setup needed to comile/run your program. But, usually it should be left blank.

Organize all your code and file in a flat structure:
–sourfile1.c
–sourcefile2.c
–main.c
–Makefile
–README

and make sure you use the provided command to compress your files under the directory that contains those files. ( that will make sure your file names are the same as what we need. And after decompression, the file will be generated into the same directory where the tgz file is placed)

Without specific explaination, you will need to provide a Makefile that can generate the executables with the name described in homework assignment. (If a Makefile was provided, please do not change the target names).


Grading

Your project will be tested to make sure it works properly – a large part of this testing will make sure that your functions generate errors (return -1) when appropriate. Here are the specifics of the grading:

Layer 2

20%

Layer 3

20%

Layer 4

20%

Layer 5

20%

Style/Code structure, etc.

10%

Submission

10%

NOTE: if you submit the homework, you will get 10%, no matter it is finished or not. 10% of your homework grade depends on the how well we can understand your code. For this assignment, it would be hard to create crummy/sloppy code (since you are forced to provide 8 individual functions). Feel free to write more than the 8 required functions, but keep in mind that we will test each of your layers in isolation (so each file needs to be a complete implementation of a layer).

The tests for each of your layers will be run independently, so errors in your layer 2 code will not propagate to your layer 3 code…


Submitting your files

Please first zip all your files using following command:

            > tar   cvzf   yourname_hw1.tgz   l2.h   l2.c   l3.h   l3.c   l4.h   l4.c   l5.h   l5.c   README

where yourname is the your clemson user name.

Please make sure you submit all your source files not the object files.  Before you submit this single file, please use the following command to check and make sure you have everything in this zip file:

            > tar   tvzf   yourname_hw1.tgz

All projects must be submitted via handin: https://handin.cs.clemson.edu

       

Don’t send compiled code!


Error report

If you find any error in this assignment, please send me an email at luofeng@clemson.edu. A correct error report will be rewarded with 1 bonus point.