Sie sind auf Seite 1von 17

NATIONAL UNIVERSITY OF SCIENCES AND TECHNOLOGY

School of Electrical Engineering and Computer Sciences


Computer Networks

Written by:
Daim Abbas Kazmi

Afrasiab Khan
Project:
User Datagram Protocol is a transportation layer protocol and is used to exchange large files
over the Internet at fast speeds such as sound transmission or video transmission. However, it
does not ensure reliable transfer but provides Best Effort Delivery as a network layer
protocol.

It is less reliable than Transmission Control Protocol TCP because there is no committed
connection and confirmations of received packets are not generally given to the sending side
and lost parcels are not re-sent i.e. retransmission. This reduces its reliability. A client can
send a document to a UDP server. Server receives and can process the document. Before the
customer sends record / message to read it, the server must be running.

In order to make UDP increasingly solid, we need to add a few features to it with the ultimate
goal that when a packet is lost it can retransmit that packet also receive packet
acknowledgments. We also need to reorder the packets on the receiver side and handle the
received duplicate packets. These features will increase the reliability of the protocol but may
reduce the speed of the protocol.

The Sender side should:

 Add sequence number to the sending packets


 Wait for packets acknowledgements from the receiver side.
 Timeout if acknowledgements are not received.
 Retransmission of unacknowledged packets.
 Fixed window size that we selected to be 5 at a time.
 Retransmission using selective repeat
 Recognize that file has ended and its time to send FIN (end of connection).

The Receiver side should:

 Send acknowledgments for the packets it has received.


 Reorder the packets as received from the sender using sequence numbers.
 Handle the packets that are received multiple times. Ignore the ones that are in excess.
 Reorder the file in such a way that received file size equals the sent file size.
 Receive FIN and terminate the connection as no more data is expected.
Sequence Numbers
The window size is selected to be 5 packets so window so we decided that for each window
1-5 sequence numbers will be enough. Each packet of data i.e. 492 bytes have a sequence and
acknowledgement number provided it using a struct named “packt”. Both seqnumber and
acknumber are 4 bytes each so the total packet should makeup of 500 bytes as required. On
sender side an array of 5 such packets are given a unique sequence number and on receiver
this array of packets is stored in a similar array. When packet is received, our program equals
the acknumber to the received seqnumber and send it to the server side. Just the acknumber is
send. This work as acknowledgements.

Retransmission (Selective Repeat)


As discussed above the receiver after receiving packets usually sends the acknowledgement
with the same sequence number. Our program works on the basis of the concept that if after
certain time the acknowledgement is not received at sender side, the packet most probably is
lost and is to be resend. This time is called timeout and we have set the timeout value to be
0.001 seconds. The timer starts after the window is all sent.
If any acknowledgement of the 5 packets is not received in this time after sending, the
program resends the packet and resets the timer and again wait for the acknowledgement of
the send sequence number. As only those packets are retransmitted whose ack is not received
this is known as selective repeat.
This is all done in receiveacks() function an acknowledgment array called ack, having size
equal to that of the window. It is initialized to 0 at start of receiving phase for each window.
When a packet is received, we set its corresponding element in ack array to 1. In case of
timeout the index having zeros are used to resend the data. We have used
“SOCK_NONBLOCK” while creating the socket that returns -1 if nothing is received by the
recvfrom() function for some time and program moves on so the program does not get stuck
if the acks are not received.
Window Size (Stop-n-Wait)
As mentioned earlier and clear from the shown figures that the window size we chose is of 5
UDP segments at a given time. Fixed window size allows the sender size to send five packets
at a time and then wait for each of their acknowledgments until then will it will not send any
further data. This approach is called stop-n-wait which is what is implemented in our system
at the sender side.

Reordering on Receiver side


Due to sending acks and retransmission from the sender side, the receiver faces a problem
that the data is not in right order and it cannot be written in the file as they are coming in. Our
program ensures that the data is correct order as it is being written in the file using sequence
numbers of the received packets. Once all the packets of a window are received on the server
side, we store them in an array of packt structure type. Then an inbuilt function of quicksort
is used to sort this using seqnumber of each packet. The comparator function plays a role in
sorting the array using void pointers. As the size of data may also vary if it is the last window
so we also have made an array to store the size of the data received in each packet. Once the
array of packt_recev() is sorted using the size array each packet is written into the file. The
written data is in order even if it is received out of order.
Duplicate packets and acks at receiver and sender respectively can also cause the sending of
packets in abnormal manner. Sometimes an acknowledgement is lost between the network
which causes the sender to retransmit same data as received and as for each window ‘for’
loop runs 5 times, recvfrom() runs only 5 times and a duplicate means not whole window is
received. This is handled using a duplicatepackt() function that compares each packet
received with those before it in a single window and if seqnumber matches to another packet
this packet is discarded. Also the counter of ‘for’ loop is decremented so the recvfrom() runs
as long as it receives packet with five different sequence numbers.

FIN and Program Termination at Both Sides


As the sender reach end of file, it needs to tell the receiver that it has reached the end and it
should not expect any more data coming from it. So, the sender side after the last packet
sends a packet containing NULL as FIN. As the receiver receive the packets of data, it keeps
checking whether the size of packet is zero or not. If the size is zero it means it has received a
FIN packet and it should close the file and no more data is coming. It sends an ack that it has
received the FIN and terminates. The sender after receiving the acknowledgement after the
FIN closes the connection and also terminates.

RUNTIME
When the program starts client-side makes a UDP socket with the already running server side
using its IP address. An array of 5 packts (special structure that contains buffer, sequence
number and acknowledgement number) is initialized. The file to be sent is opened. Elements
0-4 of array are given the sequence numbers from 1-5.
After this, 492 bytes of data is read into each of the elements from the file. After this the data
is sent one by one using the socket to the server side. After the five packets sent, the timer is
started. On the server-side array of same window size is initialized and the receiving packets
are stored in similar manner into this array named packt_recev []. On receiving each packet,
the receiver side will send an ack. The ack would be the sequence number. An array of
window size is initialized only to check for received acks. This array is initialized by zeros
and the index equaling received ack number is converted to one.
Timeout is checked after each receiving acknowledgement. If the time becomes more than
timeout i.e. 0.001 seconds in our case. All the packts having sequence number same to those
index having zeros are resend and timer is reset. The ‘while’ loop will not break until all the
acks are received. After this the client side, again read the next bytes of data into array of
packts. The sequence number remains same.
After receiving all five packets, the array of received packts are sorted by using sequence
number so the data is in order. After that the data is written such that only the data part of the
packt is written into the file. Array of size is used for this purpose. A ‘for’ loop is used to
write the ordered data into the file.
At end of file, the client sends a zero sized packet. Receiver on receiving this identify that
whole file has reached. At this point both sides would close their sockets and the files. Also,
some info is shown on terminals of both sides, like total packets sent and received, windows
exchanged and total bytes transaction.

CODE
Receiver Side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>

#define window 5
#define MAXLINE 492

struct packt{
unsigned int seqnumber;
unsigned int acknumber;
char buff[MAXLINE];
};

FILE *file;
struct packt packt_recv[window];

//Function used to sort by sequence number


int compar (const void* p1, const void* p2)
{ struct packt *e1 = (struct packt *)p1;
struct packt *e2 = (struct packt *)p2;
if ( e1->seqnumber < e2->seqnumber ) return -1;
if ( e1->seqnumber > e2->seqnumber ) return 1;
}
//Funtion to handle duplicate packets
int duplicatepackts(int s)
{
for (int t=0;t<s;t++)
{
if(packt_recv[t].seqnumber==packt_recv[s].seqnumber)
return 0;
}
return 1;
}

int main(int argc, char *argv[])


{
int sockfd;
struct sockaddr_in servaddr;

if (argc != 3)
{
printf("Error usage : %s <port_serv> <File_name>\n",argv[0]);
return EXIT_FAILURE;
}

// Creating socket file descriptor


if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 )
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}

memset(&packt_recv, 0, sizeof(struct packt)*window);


memset(&servaddr, 0, sizeof(servaddr));
int PORT=atoi(argv[1]);

// Filling server information


servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

// Bind the socket with the sender address


int len = sizeof(servaddr);
if ( bind(sockfd, (struct sockaddr *)&servaddr, len) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}

int n;
int size;
int total = 0;
int try = 0;
int i=0;
int eof = 0;
int t_bytes=0;
int n_window=0;
int packt_size[window];

file = fopen(argv[2], "wb");


//receiving file packets by packets till end of file
while(!eof)
{
//window of packets be sent at on e time
for(i=0;i<window;i++)
{
struct packt packt_send;
packt_send.seqnumber=0;

n = recvfrom(sockfd, &packt_recv[i], sizeof(struct packt), 0, (struct sockaddr *) &servaddr,


&len);
t_bytes=t_bytes+n;

// To intentionally drop a packet and check for retransmission


// if (total == 12 && !try)
// {
// try = 1;
// i--;
// continue;
// }
//If NULL packet is recieved it act as a FIN

if (n==0)
{ printf("FIN received \n" );

eof = 1;
break;
}

//Setting Ack number equal to recieved packet's sequence number


packt_send.acknumber=packt_recv[i].seqnumber;
printf("Recieved Sequence Number: %d Size: %d \n",packt_recv[i].seqnumber,n);

//Checking for duplicates


if(!duplicatepackts(i))
{ printf("Duplicate packet recieved: %d\n",i );
i--;

}
packt_size[packt_recv[i].seqnumber-1]=n;
//sending acknowledgements
printf("Sending ack\n");
sendto(sockfd,&packt_send.acknumber, 4, 0, (struct sockaddr *) &servaddr, len);
total++;
}
n_window++;
//reordering
qsort(&packt_recv[0],i,sizeof(struct packt),compar);

//Writing packets into the file


for (int j = 0 ; j < i ; j++)
{
fwrite(packt_recv[j].buff, 1, packt_size[j]-8, file);
}

}
printf("Total bytes recieved: %d\n",t_bytes );
printf("Total packets: %d\n",total );
printf("Total windows: %d\n",n_window );
printf("Success");

fclose(file);
close(sockfd);
return 0;
}

Sender Side
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>

#define MAXLINE 492


#define window 5

struct packt
{
unsigned int seqnumber;
unsigned int acknumber;
char buff[MAXLINE];
};

FILE *file;
struct sockaddr_in servaddr;
struct packt packt_send[window];

int allnottrue(int* ack, int n)


{
for (int i=0; i<n;i++)
{
if(ack[i]==0) return 1;
}
return 0;
}

//Function to check the recieving acknowledgements and Retransmission


void receiveacks(int sfd,clock_t start, int n)
{
int len,k;
struct packt recvpackt;
int ack[n];

for (int i=1;i<n;i++)


{
ack[i]=0;
}

while (allnottrue(ack, n))


{
//Receiving acks
k=recvfrom(sfd, &recvpackt.acknumber,4, 0, (struct sockaddr*)&servaddr,&len);

//Updating Acknowledgement array


if (k>0)
{
printf("Ack recieved: %d\n", recvpackt.acknumber);
ack[recvpackt.acknumber-1]=1;
continue;
}
clock_t end=clock()/CLOCKS_PER_SEC;
int dif=end-start;

//Timeout handling
if (dif>0.001)
{
printf("Error at acknowledgment: Timeout \n");
for (int i=0;i<n;i++)
{
if (ack[i]!=1)
{
printf("Timed out packet in window: %d \n",i+1);
//Retransmission of lost Packet
sendto(sfd,&packt_send[i],sizeof(packt_send),0,(struct sockaddr*)&servaddr,len);
printf("Resending data: %d \n",i+1);
}
}
//Resetting timer
start=clock()/CLOCKS_PER_SEC;
}
}

int main(int argc, char *argv[])


{
int sockfd;
if (argc != 4)
{
printf("Error usage : %s <IP_address> <port_serv> <file_name>\n",argv[0]);
return EXIT_FAILURE;
}

// Creating socket file descriptor


if ( (sockfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0)) < 0 )
{
perror("socket creation failed");
exit(EXIT_FAILURE);
}

memset(&servaddr, 0, sizeof(servaddr));
memset(&packt_send, 0, sizeof(struct packt)*window);

// Filling server information


int PORT=atoi(argv[2]);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(argv[1]);

int n;
int len = sizeof(servaddr);
int nread;
clock_t start;
int eof = 0;
int k;
int t_bytes=0;
int n_windows=0;
int total=0;

file = fopen(argv[3],"rb");
if(file == NULL)
{
printf("Error Opening the file\n");
return 0;
}

//Sending packets segment by segment until end of file


while(!eof)
{
//initializing Sequence numbers
for (int i=0;i<window;i++)
{
packt_send[i].seqnumber=i+1;
}

int count = 0;
//initializing data
for(int j=0;j<window;j++)
{
//Reading data from file into buffers
nread = fread(packt_send[j].buff , 1, MAXLINE, file);
int size = (void*)&packt_send[j].buff[nread] - (void*)&packt_send[j];
//Sending packets
t_bytes=t_bytes+size;
k=sendto(sockfd, &packt_send[j], size, 0, (struct sockaddr *) &servaddr, len);
printf("Sequence Number: %d Size:%d \n",packt_send[j].seqnumber,size );
count++;
total++;
//If whole file is sent and its time to send FIN
if(nread < MAXLINE)
{
if(feof(file))
{
eof = 1;
break;
}
}
if(ferror(file))
{
printf("Error Reading\n");
}
}
n_windows++;
//Starting timer as last packet is sent
start=clock()/CLOCKS_PER_SEC;
//Function call
receiveacks(sockfd,start,count);
}

//Sending FIN
printf("FIN sent.\n");
sendto(sockfd, NULL, 0,0, (const struct sockaddr *) &servaddr, sizeof(servaddr));

printf("Total bytes sent: %d\n",t_bytes );


printf("Total packets: %d\n",total );
printf("Total windows: %d\n",n_windows );
printf("File Transferred successfully\n");

//Closing the file


fclose(file);
close(sockfd);
return 0;
}

Das könnte Ihnen auch gefallen