[0810 Assignment] UDP-based TFTP file transfer (download, upload)

1. Overview of tftp protocol

Simple File Transfer Protocol, a set of standard protocols for file transfer on the Internet, using UDP for transmission.

Features:

① is the application layer protocol

② Implementation based on UDP protocol

③ Data transmission mode

  • octet: binary mode (commonly used)
  • mail: no longer supported

2. tftp download model

3. tftp protocol analysis

Error code:
    0 undefined, error message
    1 File not found.
    2 Access violation.
    3 Disk full or allocation exceeded.
    4 illegal TFTP operation.
    5 Unknown transfer ID.
    6 File already exists.
    7 No such user.
    8 Unsupported option(s) requested.

4. Summary of TFTP communication process

  1. The server waits for the client’s request on port 69
  2. If the server approves the request, it will use the ephemeral port to communicate with the client.
  3. The number of each packet changes (starts from 1)
  4. Each data packet must be confirmed by ACK. If a timeout occurs, the last data packet or ACK packet needs to be resent
  5. The data length is transmitted in 512Byte, and the data less than 512Byte means that the data transmission is over.

5. Client download file (code)

include <stdio.h>
include <head.h>
include <sys/types.h>
include <sys/socket.h>
include <arpa/inet.h>

define PORT 69 //The port number bound by the server
define IP "192.168.1.109" //"192.168.122.120" //IP address of the server
define FILENAME "5.png"

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

   //Create a message socket
   int cfd = socket(AF_INET, SOCK_DGRAM, 0);
   if(cfd < 0)
   {
       ERR_MSG("socket");
       return -1;
   }
   printf("cfd = %d\\
",cfd);

   // Bind the address information structure of the client to the socket ---> binding is not required
   //If not bound, the operating system will bind the client to the IP of the running host and a random port number

   //Fill the address information structure of the server to be connected, the real address information structure is formulated according to the address family
   //Whoever you want to send to, fill in the address information of the person you want to send it to.
   //AF_INET : man 7 ip
   struct sockaddr_in sin;
   socklen_t addrlen=sizeof(sin);
   sin.sin_family = AF_INET; //must fill in AF_INET
   sin.sin_port = htons(PORT); //port number: the port number bound by the server
   sin.sin_addr.s_addr = inet_addr(IP);//Server bound IP

   //Edit read and write requests
   char buf[516]="";
   char *ptr=buf;
   unsigned short *p1=(short*)buf; //Operation code
   *p1=htons(1);
   char *p2=buf + 2; //File name
   strcpy(p2,FILENAME);
   char *p3=p2 + strlen(p2); //The first 0
   *p3=0;
   char *p4=p3 + 1; //Mode
   strcpy(p4,"octet");
   size_t size=2 + strlen(p2) + 1 + strlen(p4) + 1; //opcode + filename + 0 + mode + 0

   size_t res=0;

   //Send download request
   if(sendto(cfd,buf,size,0,(struct sockaddr*) & amp;sin,sizeof(sin))<0)
   {
       ERR_MSG("sendto");
       return -1;
   }

   //Create download file and clear it
   int fd=open("./1.png",O_WRONLY|O_CREAT|O_TRUNC,0664);
   if(fd < 0)
   {
       perror("open");
       return -1;
   }

   //Cycle send and receive, receive files, send ack
   while(1)
   {
       //receive packet
       res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*) &sin, &addrlen);
       if(res < 0)
       {
           ERR_MSG("recvfrom");
           return -1;
       }
       if(write(fd,buf + 4,res-4) < 0 )
       {
           perror("write");
           return -1;
       }

       //send ACK
       *p1=htons(4);
       if(sendto(cfd,buf,4,0,(struct sockaddr*) & amp;sin,addrlen) < 0)
       {
           ERR_MSG("sendto");
           return -1;
       }
       if(res-4 < 512)
       {
           printf("download success\\
");
           break;
       }
   }
   close(cfd);
   close(fd);
   return 0;
} 

6. [Full] code (client download, upload file)

#include <stdio.h>
#include <head.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

#define PORT 69 //The port number bound by the server
#define IP "192.168.1.107" //IP address of the server

int do_download(int cfd,struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);

int main(int argc, const char *argv[])
{
//Create a report socket
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("cfd = %d\\
",cfd);

// Bind the address information structure of the client to the socket ---> binding is not required
//If not bound, the operating system will bind the client to the IP of the running host and a random port number

//Fill the address information structure of the server to be connected, the real address information structure is formulated according to the address family
//Whoever you want to send it to, fill in the address information
//AF_INET: man 7 ip
struct sockaddr_in sin;
socklen_t addrlen=sizeof(sin);
sin.sin_family = AF_INET; //must fill in AF_INET
sin.sin_port = htons(PORT); //port number: the port number bound by the server
sin.sin_addr.s_addr = inet_addr(IP); //Server bound IP

char choose =0;
while(1)
{
printf("------------------------\\
");
printf("---------1. Download --------\\
");
printf("---------2. Upload--------\\
");
printf("---------3. Exit-------\\
");
printf("------------------------\\
");
printf("------------------------\\
");
printf("Please enter >>> ");

choose = getchar();
while(getchar() != 10); //Loop to get characters until \\
 is encountered to end the loop

switch(choose)
{
case '1':
do_download(cfd,sin);
break;
case '2':
do_upload(cfd,sin);
break;
case '3':
goto END;
break;
default:
printf("Input error! Please re-enter\\
");

}
}
END:
//Close file descriptor
close(cfd);

return 0;
}

int do_download(int cfd, struct sockaddr_in sin)
{
//The package is ready to send the download request
char buf[516]="";
char name[20]="";
printf("Please enter the file name to download >>> ");
scanf("%s",name);
while(getchar()!=10);

unsigned short *p1=(short*)buf; //opcode
*p1=htons(1);

char *p2=buf + 2; //file name
strcpy(p2,name);

char *p3=p2 + strlen(p2); //the first 0
*p3=0;

char *p4=p3 + 1; //mode
strcpy(p4,"octet");

size_t size=2 + strlen(p2) + 1 + strlen(p4) + 1; //opcode + filename + 0 + mode + 0

//Send download request
if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*) & amp;sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}

//Create the download file and clear it
int fd = -1; // must be initialized to an invalid file descriptor
socklen_t addrlen = sizeof(sin);
ssize_t res = 0;
unsigned short num = 0; //Record the local block number

//Send download request
while(1)
{
\t\t//Receive data
bzero(buf, sizeof(buf));
res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*) &sin, &addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}

if(3 == buf[1]) //data packet
{
//Determine whether the block number of the data packet returned by the server is consistent with the locally recorded block number
if(*(unsigned short*)(buf + 2) == htons((num + 1)))
{
num + + ; //Update the block number of the local record
if(-1 == fd)
{
fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd < 0)
{
perror("open");
return -1;
}
}

//write data to file
if(write(fd,buf + 4,res-4) < 0 )
{
perror("write");
return -1;
}

//send ACK
buf[1] = 4;
//*p1=htons(4);
if(sendto(cfd,buf,4,0,(struct sockaddr*) & amp;sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//If the received data is less than 512, jump out of the loop and end the download.
if(res-4 < 512)
{
printf("%s file downloaded\\
",name);
break;
}
}
}
else if(5 == buf[1]) // error package
{
printf("Error: %d %s\\
",ntohs(*(short*)(buf + 2)),buf + 4);
close(fd);
return -1;
}
}
close(fd);
return 0;
}


int do_upload(int cfd, struct sockaddr_in sin)
{
//The package is ready to send the upload request
char buf[516]="";
char name[20]="";
printf("Please enter the file name to upload >>> ");
scanf("%s",name);
while(getchar()!=10);

int fd = open(name,O_RDONLY);
if(fd < 0)
{
if(errno == ENOENT)
{
printf(">>>File does not exist, please re-enter <<<\\
");
return -2;
}
else
{
ERR_MSG("open");
return -1;
}
}
//The package is ready to send the upload request
unsigned short *p1=(short*)buf; //opcode
*p1=htons(2);

char *p2=buf + 2; //file name
strcpy(p2,name);

char *p3=p2 + strlen(p2); //the first 0
*p3=0;

char *p4=p3 + 1; //mode
strcpy(p4,"octet");

size_t size=2 + strlen(p2) + 1 + strlen(p4) + 1; //Operation code + file name + 0 + mode + 0

//Send upload request
if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*) & amp;sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}

//Receive and send packets in a loop
ssize_t res;
unsigned short num = 0;
socklen_t addrlen = sizeof(sin);
while(1)
{
//Read data from file into buf
bzero(buf, sizeof(buf));
res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*) & amp;sin, & amp;addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}

//The range of the opcode is 1-5 because it is network byte order
//So the valid opcode is stored in the high bit, that is, the position of buf[1]
//printf("buf[1] = %d\\
",buf[1]); //4 The server returns the response packet
if(4 == buf[1]) //packet
{
//Determine whether the number of the current data packet is equal to the number of the response packet
//Prevent packet loss or repeated packet receipt during transmission
if(num == ntohs(*(unsigned short*)(buf + 2)))
{
//Modify opcode to data packet
buf[1] = 3;
//fill block number
num + + ;
*(unsigned short*)(buf + 2) = htons(num);

//Read data
res = read(fd,buf + 4,sizeof(buf)-4);
if(res < 0)
{
ERR_MSG("read");
return -1;
}
else if(0 == res)
{
printf("%s file upload completed!\\
",name);
break;
}

//Send packet
//The size of the sent data packet is, the number of bytes read res + opcode 2byte + block number 2bytes
if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*) & amp;sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
}
else
{
printf("File upload failed, please check the network environment\\
");
break;
}
}
else if(5 == buf[1]) //Error package
{
printf("Error: %d %s\\
",ntohs(*(short*)(buf + 2)),buf + 4);
close(fd);
return -1;
}
}
close(fd);
return 0;
}

download:

Upload: