CS 5785 Project: Remote Copying of Files


Section 1: Problem Statement

This project deals with transferring files using a client-server model. A file of ASCII characters is transferred from a client to a server. The program has a built-in CRC (cyclic redundancy check) polynomial error checking method. The client encodes the error detecting message into a frame along with the character from the file. When the server receives the frame, it decodes the message. If it finds that an error occurred, it requests a retransmission of the frame. If no erros occurred, the server writes the character to another file. When the entire file is received, the server sends the file to a second client, which also checks for errors and copies the file in the same manner as the first client to the server.


Section 2: Description of Algorithm

The project is separated into several different functions to accomplish the task of transferring files. The functions and their descriptions are as follows:


CRC.h:

The header file contains the CRC polynomial to be used in the error detecting process and its length. The CRC-16 is used. This was chosen over other CRC polynomials because with the other polynomials, some of the errors will not be detected since some of the binary strings of the ASCII values will be exactly divisible by the CRC. Another thing contained in the header file is the port number, which must be in the range of 2^13 to 2^16-1. This allows the program to be run again right away, instead of waiting for the bind address to no longer be in use.


ASCII_to_Binary:

The major goal of this function is to convert a 7-bit ASCII character into a string of 1's and 0's. It keeps dividing the ASCII number by 2, storing each remainder (either 1 or 0) into a string. It continues this process until the dividend is 0. The string of 1's and 0's that was created is in the reverse order, so the function Reverse is used to reverse the order of the bits in the string. The ASCII number is now represented as a binary number.


Reverse:

This function is used by ASCII_to_Binary. The binary string calculated by that function has its bits reversed, so this function reverses the bits to obtain the correct binary representation of the ASCII value.


Divide:

The procedures of this function are basically the encoding process. The binary string representing the 7-bit ASCII number is divided by the CRC polynomial and the remainder is obtained. The binary string comes in padded with zeros since it must be at least as long as the CRC, and this is done by Insert_Trailing_0s. The division process repeats until the entire binary string has been divided out. This works much like normal division, each successive remainder is found by subtracting the CRC from the previous remainder, which is actually the same as the XOR operation in binary. This is done by the XOr function. Each iteration, the next digit is brought down from the dividend and added on to the end of the remainder string. This is done by the Append_Char function. Each time the remainder is found from the XOR process, all leading zeros are removed from the string. This is done by the Remove_Leading_0s function to keep the length of the remainder the same as the length of the CRC. The final results after the complete division is the remainder, which is the error detecting message to be sent in the frame.


Append_Char:

The next character of the dividend is appended to the remainder string of each iteration of the division process.


XOr:

The XOR of the CRC and the remainder of each iteration is calculated. This is part of the division process.


Insert_Trailing_0s:

The binary string representing the 7-bit ASCII character is padded with zeros because its length must be at least that of the CRC.


Remove_Leading_0s:

This function is used in the division method. After XORing the CRC with the previous remainder, some leading zeros may result. These are not needed by the division process and they affect the length of the string, so they are removed.


Insert_Leading_0s:

The resulting remainder of the division process must have the same number of bits as the original binary string, so that the two can be XORed properly. This function adds zeros at the beginning of the remainder string to make it the needed length.


Add_to_Frame:

This is where the frame that is to be sent is built. The frame is a string of characters. The first character is the actual ASCII character from the file. The rest of the string is the message encoded by the division method. This is for the error detection.


Remove_From_Frame:

This function is used by the receiving end after the frame is received. The ASCII character is separated from the encoded message.


Encode:

This function controls the encoding of the error detecting message for the frame. It makes the necessary calls to functions in carrying out this process. The ASCII character is converted to binary and divided by the CRC polynomial. The resulting remainder is XORed with the binary string and the frame is built. The client uses this function in the file transfer process.


client1_in:

The actual networking process is dealt with here. A socket is created, a frame is built and sent, and the socket is closed.


server_in:

This also deals with the networking process. A socket is opened and the server waits for the client to send the frame. When the frame is received, the message is decoded by dividing it by the CRC. If the result is the zero polynomial, there was not a transmission error and the ASCII character from the frame is written to a file. It also sends an acknowledgment to the client to let it know the frame was received without error. If the division result was not zero, then an error occurred and the server requests for the frame to be re-transmitted. After the entire file is received, the server transmits it to a second client, in the same manner that the first client transmitted the file to the server.


client2_in:

The second client received the file from the server. When a frame is received, it decodes the message and writes the character to a file if no error has occurred, otherwise it sends an acknowledgment that requests for a retransmission of the frame. When the file is all received and written to another file, the socket is closed and the transferring process is complete.


Section 3: Software and Hardware Used

The software used was client_in and server_in. These were functions used in a previous lab that were modified for this project. The client_in function was used to create both client1_in and client2_in. No significant hardware was used besides a PC.


Section 4: Test Cases and Test Results

Each function was tested separately before incorporating all of them into the program. The ASCII_to_Binary function was tested with several different ASCII values. The Reverse, Append_Char, XOr, Remove_Leading_0s, Insert_Leading_0s, and Insert_Trailing_0s functions were all tested by using random binary strings. These functions dealt with string manipulation, so not many test cases needed to be used.

The Divide function required a fair amount of testing. Several different ASCII values were chosen. It was especially important to test the case of a NULL character, since the binary representation is a string of all zeros. It had to be ensured that this worked properly as it did for all other ASCII values. Although the occurrence of a NULL character in a file is not common, it was important to ensure that the Division algorithm worked for any ASCII value that could possibly be in a file. Several ASCII values were tested to make certain that the Division method and all of its auxiliary functions worked correctly together. The results of the functions' computations were also compared to the result from manually dividing the binary strings on paper.

The testing for the Add_to_Frame and Remove_From_Frame functions was similar to that of the Division function. Several ASCII values were used for the testing. The frame, the character, and the polynomial message were each printed to make sure their values were correct. The integer values for the characters in the message were also printed as another check. This was especially important for the case of the NULL character. Since the NULL character appears at the beginning of the frame, the rest of the string will not be printed. By printing the integer values, it was made certain that this special case was working correctly.

The program was initially tested for a specifically defined ASCII character. When that was working, a loop was added to test the process for an entire file. All of the above functions were tested before any of the actual networking functions were included. All the functions were included in a separate makefile from that of the networking functions.

The server_in, client1_in, and client2_in functions did not need as much testing since they were used in a previous program. All the code that dealt with opening and closing sockets was already functioning correctly. The parts that needed to be tested the most were the encoding and decoding portions. The encoding process occurred in the first client. This was a simple modification of a main function used early in the project process. It is mostly made up of the function calls necessary for encoding and building the frame. For the decoding process in the server, not much testing was needed since the main task was to make a call to Divide. The code for sending acknowledgments was added later. This was tested with simple cases, to make sure the values were being sent and received correctly, before it was included in the whole transfer process. Throughout this project, the CRC-3 was mostly used, because it is much easier to work with, wince no nearly as many bits are involved. When it was sure that the functions were working correctly, the CRC-16 polynomial was used for testing and everything worked the way it should.

One problem was with the makefiles. When all functions were included in one makefile, it produced fatal errors to occur during compilation, and the code failed to be compiled. To resolve this problem, only the client and server functions were left in the makefile. The other functions were #included in the source codes for the clients and the server. Another problem was of functions being multiply defined. This would happen if a function was included in two different files. For example, the XOr function was included in the Divide function. It could not also be included in the client source code because a multiply defined error would result.

The biggest problem was getting the file to transfer from the server to the second client. All of the send and receive commands had to be carefully integrated, otherwise the connections between client and server would fail. It was important to make a distinction between which sockets were to be used at which times. For awhile, the server appeared to send frames correctly, but the receiver did not seem to receive them properly. It would receive the first frame correctly, then it would stop there, and never copy any characters to the file. Although it took a long time to find the error, it was a simple mistake. Some of the code that was used to close the sockets was accidently included before the code for receiving the frames. Thus, the socket was being closed before most of the file was even transferred. This portion of code was simply moved to come after the code that dealt with tranformation, and everything worked perfectly. Some portions of code were copied from other modules and so it is important to ensure that they are copied into the place in which they belong.


Section 5: Sources Used

Tanenbaum, Andrew S. Computer Networks Prentice Hall 1996: 186-190.

Kernighan, Brian W., Ritchie, Dennis M. The C Programming Language Bell Telephone Laboratories, Inc. 241-258.

Mystic Canyon Software. Mystic Pascal User Manual 1986: 16 (ASCII Table).