This homework involves the development of C code to support a layered communication system. You are provided with the interface protocols (C function prototypes) for each of the layers, your job is to implement these layers - part of this will involve the development of peer-to-peer protocols. You are responsible for providing the C functions that provide layers 2,3 and 4 (described below). You do not need to provide a layer 1 implementation - I've included a sample along with a sample main program. When testing 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!
.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 maintains the order of individual bytes.
The layer1 interface protocol is specified here, but you don't need to write this layer - just use it. A sample implemenation is provided that will support testing of your code - the sample layer will support half-duplex communication using a pipe.
The layer1 API is shown below - each function returns an int that indicates the number of bytes read/written, in this case the return value should be 1 if everything goes well, anything else indicates some kind of error.
int l1_write(char b);
writes the byte specified by
b. Returns 1 on success, anything else is an error.
int l1_read(char *b);
Reads one byte and
copies the byte to the address specified by b. Returns 1
on success, anything else indicates an error.
Layer 2 provides transmission/reception of a sequence (block) of bytes. Return values indicate the number of bytes read/written, or -1 if an error occurs. The maximum block size handled by layer2 is 999 bytes (characters).
Important: The service provided by layer 2 is more than just making it easy to send/receive more than 1 byte (which is all layer 1 does), layer 2 sends a chunk of bytes to the receiving layer 2. The chunk of bytes can be considered a message, and the service provided here is transmission/reception of a message. The point is that the peer-to-peer protocol used must include some mechanism for allowing the receiving end to know when it has a complete message.
int l2_write(char *b,int n);
Sends a block of
bytes of length n. The bytes sent are specified
by the pointer b.
Returns the number of bytes sent on success, -1 means an error.
int l2_read(char *b,int max);
Reads a block of
bytes in to memory starting at the address specified by the pointer
b. No more than max bytes will be put in to
memory, so max limits the size of the message read. If a
message is received by l2_read that would require more than
max bytes, l2_read should return -1 (error).
Upon successful reception of a message, the size of the message (the
number of bytes stored in b) is returned.
Layer 3 adds simple error detection to the services provided by layer 2. 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.
Error Detection: We discussed a few ways of detecting transmission errors in class, 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 character 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 means you want to detect errors, a single byte checksum is just the easiet 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 l3_write(char *b,int n);
Sends a block of
bytes of length n. The bytes sent are specified
by the pointer b.
Returns the number of bytes sent on success, -1 means an error.
int l3_read(char *b,int max);
Reads a block of
bytes in to memory starting at the address specified by the pointer
b. No more than max bytes will be put in to
memory, so max limits the size of the message read. If a
message is received by l2_read that would require more
than max bytes, l2_read should return -1
(error). Some error detection mechanism is used to detect
transmission errors. If such an error is detected by
l3_read, a -1 will be returned. Upon successful reception
of a message, the size of the message (the number of bytes stored in
b) is returned.
Layer 4 provides two different services, a simple string delivery service and an encrypted string delivery service. The general idea is that the peer processes can use layer 4 services to exchange text strings using only the services provided by layer 3. The encrypted service does not need to involve any real (powerful) encryption - as long as the actual string is not sent in "plain text" any scrambling algorithm you come up with is fine.
NOTE: The layer 4 services handle strings, and in C this means null terminated strings. The layer 4 writing functions expect null-terminated strings and the layer 4 reading functions should also deal with null terminated strings (so your reading functions have to make sure the caller gets a null terminated string).
int l4_write_string(char *s);
Sends a null-terminated string. Returns the number of bytes sent on success, -1 means an error.
int l4_write_estring(char *s);
Sends a null-terminated string using some encryption
algorithm. The string itself is not sent as "plain-text", but is
somehow scrambled so that an evesdropper (without knowledge of the
scrambling mechanism) could not reproduce the original string. Note
that the string provided to l4_write_estring is not
already scrambled - this function does the scrambling and sends the
scrambled string. Returns the number of bytes sent on success, -1
means an error occured.
int l4_read_string(char *b,int max);
Returns
the number of bytes read, and fills in memory starting at the address
b with the received null-terminated string. Returns -1
if the received string is larger than max (and does not
put more than max bytes in memory).
int l4_read_estring(char *b,int max);
Returns
the number of bytes read, and fills in memory starting at the address
b with the received null-terminated string. This layer 4
function assumes that the sender is sending a scrambled string (using
l4_write_estring and unscrambles the string before
returning. Returns -1 if an error occurs or if the received string is
larger than max (and does not put more than
max bytes in memory).
Included below is a sample main program that should give you an idea of how to test your code. The code is also available here Note that although the code below only accesses the layer 4 functions, 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 trasmission errors by providing a buggy layer 1).
The test code below might be used as part of an application that
requires that a username and password be sent from a client to a
server. The username is sent as plain-text, and the password is
scrambled. The following main program calls layer 4 functions to send
or receive these messages depending on whether it finds any command
line arguments present. If there are command line arguments the
program
assumes it is a sender and sends argv[1]
followed by a scrambled argv[2]. If no command line
arguments are detected, the program first reads a string using layer
4,
then reads an encrypted string, and prints them both out to STDOUT.
Using the sample layer 1 code included, 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:
> ./hw1 username password | ./hw1
The code is also available here: hw1.c
#include <stdio.h>
// Sample layer 1 implementation - this can be used to
// provide half-duplex communication simply by using the shell to
// create a pipe between a sender process and a receiver process.
// sample l1_read just calls read on stdin
int l1_read( char *b) {
return(read(0,b,1));
}
// sample l1_write just calls write to stdout
int l1_write(char b) {
return(write(1,&b,1));
}
// prototypes for the layer 4 functions (you have to provide these
// functions!)
int l4_write_string(char *);
int l4_write_estring(char *s);
int l4_read_string(char *, int);
int l4_read_estring(char *, int);
// This is a sample test program that uses all the layer 4
// functions. This program does one of the following:
//
// if there are 2 command line arguments, it sends the first
// argument using l4_write_string and the second using
// l4_write_estring. Then the program exits.
//
// if there are not 2 command line arguments, the program assumes
// it is reading, so it first calls l4_read_string to get the first
// string sent, then calls l4_read_estring. Both strings are printed
// out to standard output.
int main(int argc,char **argv) {
char buf[1000];
if (argc!=3) {
// I'm a reader - read the username
if (l4_read_string(buf,999)==-1) {
fprintf(stderr,"Reading error\n");
exit(1);
}
printf("Username: %s\n",buf);
// now get the password
if (l4_read_estring(buf,999)==-1) {
fprintf(stderr,"Reading error\n");
exit(1);
}
printf("Password: %s\n",buf);
} else {
// I'm a writer - first send argv[1] as plain text
if (l4_write_string(argv[1])==-1) {
fprintf(stderr,"error sending username\n");
exit(1);
}
// now send second parameter scrambled
if (l4_write_estring(argv[2])==-1) {
fprintf(stderr,"error sending password\n");
exit(1);
}
}
return(0);
}
|
You must provide us with one file for each of the 3 layers you
will write. The layer 2 code must be in a file named
l2.c,
the layer 3 code in l3.c and the layer 4 code in
l4.c.
NOTE:If you've never split a C program among
multiple files before - here is how you would build the program.
Assuming you save the sample main/layer 1 code in a file named
hw1.c,you can use gcc to build the program like this:
> gcc -o hw1 hw1.c l2.c l3.c l4.c
The executeable program produces will be hw1, so you
could then test the sender with something like this:
> ./hw1 uname passwd
You must also include in your submission a file named
README
that includes a brief description of your submission, including the
name of each file submitted along with a one line description of what
is in the file. You should also mention what Operating System you used
to develop the code, and any special instructions for building your
code, problems you had or anything else you think might be helpful to
us. You do not need to provide any code for layer1 or a main
program, we will supply our own when testing your code.
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.
25% of your homework grade depends on the how well we can understand your code - this means everything should be commented! Use the above code as a guide to the level of commenting we think is reasonable.
Submission of your homework is via email, the general idea is to send an email message with all your files as attachments. There is an automated email submission system that will respond to your submission right away, so you will have a record that we got your files.
All projects must be submitted via email to netprog-submit@cs.rpi.edu. The subject line of the submission message should contain a single number indicating the project number (1 for HW1, 2 for HW2, etc.). You must include your files as attachments, feel free to send a zip-file or a tar file.
Don't send compiled code!
You can expect a return email indicating receipt of your project submission immediately. This receipt will include a list of all the files that were successfully extracted by the submission script - please look over the receipt carefully to make sure your submission worked.
Multiple Submissions: You can resubmit up to 10 times for each project, we will always grade the last submission received unless you tell us otherwise.