1. UDP-based TFTP file transfer upload and download full version

1) Overview of tftp protocol

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

Features:

is an application layer protocol

Realized based on UDP protocol

data transfer mode

octet: binary mode (commonly used)

mail: no longer supported

2) tftp download model

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.

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.

#include <head.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define PORT 69 //1024~49151
#define IP "192.168.122.49" //
 
int do_download(int sfd, struct sockaddr_in sin);
int do_upload(int sfd, struct sockaddr_in sin);
 
int main(int argc, const char *argv[])
{
    //Create a message socket
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("socket create success sfd=%d __%d__\\
", sfd, __LINE__);
 
    //Binding--->Non-required binding
    //If not bound, the operating system will automatically bind the local IP, and randomly select an unused port number from the range of 49152~65535
    
    //Fill the address information structure of the server
    //For the use of sendto below,
    struct sockaddr_in sin;
    sin.sin_family = AF_INET; //must fill in AF_INET;
    sin.sin_port = htons(PORT); //The port bound by the server, network byte order
    sin.sin_addr.s_addr = inet_addr(IP); //Server bound IP, local IP ifconfig
    
    char choose = 0;
    while(1)
    {
        printf("----------------------------\\
");
        printf("----------1. Download -----------\\
");
        printf("----------2. upload -----------\\
");
        printf("----------3. exit -----------\\
");
        printf("----------------------------\\
");
        printf("Please enter >>> ");
        choose = getchar();
        while(getchar()!=10);
 
        switch(choose)
        {
        case '1':
            do_download(sfd, sin);
            break;
        case '2':
            do_upload(sfd, sin);
            break;
        case '3':
            goto END;
            break;
        default:
            printf("Input error, please re-enter\\
");
            break;
        }
    }
END:
    //Close all file descriptors
    close(sfd);
    return 0;
}
 
int do_upload(int sfd, struct sockaddr_in sin)
{
    int ret_val = 0;
    char filename[20] = "";
    printf("Please enter the file name to upload >>> ");
    scanf("%s", filename);
    while(getchar()!=10);
 
    //Check if the file exists
    int fd = open(filename, O_RDONLY);
    if(fd < 0)
    {
        ERR_MSG("open");
        return -1;
    }
 
 
    //Send upload request
    //Group upload request package
    char buf[516] = "";
    char *ptr = buf;
unsigned short *p1 = (unsigned short*)buf;
*p1 = htons(2);
char *p2 = buf + 2;
strcpy(p2, filename);
char *p3 = p2 + strlen(p2) + 1;
strcpy(p3,"octet");
// calculate the size
int size = 2 + strlen(filename) + 7;
    //sendto
    if(sendto(sfd, buf, size, 0, (struct sockaddr*) & amp;sin, sizeof(sin)) < 0)
    {
        ERR_MSG("sendto");
        return -1;
    }
 
    socklen_t addrlen = sizeof(sin);
    ssize_t res = 0;
    unsigned short num = 0; //The short number of the local record
 
    while(1)
    {
        bzero(buf, sizeof(buf));
        //Receive the response from the server
        res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*) & amp;sin, & amp;addrlen);
        if(res < 0)
        {
            ERR_MSG("recvfrom");
            ret_val = -1;
            break;
        }
        //printf("res = %ld __%d__\\
", res, __LINE__);
        //printf("0:%d 1:%d 2:%d 3:%d\\
", buf[0], buf[1], buf[2], buf[3]);
    
        if(4 == buf[1])
        {
            if(htons(num) == *(unsigned short*)(buf + 2))
            {
                //Group data packet to server
                num++;
                *(unsigned short*)buf = htons(3);
                *(unsigned short*)(buf + 2) = htons(num);
 
                res = read(fd, buf + 4, 512);
                if(res < 0)
                {
                    ret_val = -1;
                    break;
                }
                else if(0 == res)
                {
                    printf("File: %s uploaded\\
", filename);
                    break;
                }
 
                //send data packet
                if(sendto(sfd, buf, res + 4, 0, (struct sockaddr*) & amp;sin, sizeof(sin)) < 0)
                {
                    ERR_MSG("sendto");
                    return -1;
                }
            }
        }
        else if(5 == buf[1])//error package
        {
            printf("MSG_ERR: code[%d] msg[%s] __%d__\\
", \
                    ntohs(*(unsigned short*)(buf + 2)), buf + 4, __LINE__);
            ret_val = -1;
            break;
        }
 
    }
 
    close(fd);
    return ret_val;
}
 
 
 
 
int do_download(int sfd, struct sockaddr_in sin)
{
    int ret_val = 0;
    char buf[516] = "";
 
    char filename[20] = "";
    printf("Please enter the file name to download >>> ");
    scanf("%s", filename);
    while(getchar()!=10);
 
    //Send download request
    //group protocol package
    char *ptr = buf;
unsigned short *p1 = (unsigned short*)buf;
*p1 = htons(1);
char *p2 = buf + 2;
strcpy(p2, filename);
char *p3 = p2 + strlen(p2) + 1;
strcpy(p3,"octet");
// calculate the size
int size = 2 + strlen(filename) + 7;
    //sendto
    if(sendto(sfd, buf, size, 0, (struct sockaddr*) & amp;sin, sizeof(sin)) < 0)
    {
        ERR_MSG("sendto");
        return -1;
    }
 
    //Create and open the file to be downloaded locally
    int fd = -1; // must initialize a meaningless file descriptor, otherwise the following close
 
    socklen_t addrlen = sizeof(sin);
    ssize_t res = 0;
    unsigned short num = 0; //The short number of the local record
 
    //Receive data packets in a loop and reply to ACK
    while(1)
    {
        bzero(buf, sizeof(buf));
        //receive packet
        res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*) & amp;sin, & amp;addrlen);
        if(res < 0)
        {
            ERR_MSG("recvfrom");
            ret_val = -1;
            break;
        }
        //printf("res = %ld __%d__\\
", res, __LINE__);
 
        //printf("0:%d 1:%d 2:%d 3:%d\\
", buf[0], buf[1], buf[2], buf[3]);
 
        //if(ntohs(*(unsigned short*)buf) == 3)
        //Because the opcode occupies two bytes of unsigned integer, it is transmitted in big endian when transmitting
        //All valid error codes will be stored in the high address, that is, stored in buf[1], and 0 is stored in buf[0]
 
        if(3 == buf[1]) //data packet
        {
            //UDP may duplicate data, in order to prevent duplicate packets.
            //Record the fast number returned by the server locally. Before each processing, first judge whether the fast number is correct
            if(htons(num + 1) == *(unsigned short*)(buf + 2))
            {
                num++;
 
                if(-1 == fd)
                {
                    fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
                    if(fd < 0)
                    {
                        ERR_MSG("open");
                        ret_val = -3;
                        break;
                    }
                }
 
                //Write the obtained data to the file
                if(write(fd, buf + 4, res-4) < 0)
                {
                    ERR_MSG("write");
                    ret_val = -3;
                    break;
                }
 
                //Reply ACK, the first four bytes of the data packet are basically the same as the ACK packet
                //Only the opcode is inconsistent, the valid opcode is at the position of buf[1], just change buf[1] from 3 to 4.
                buf[1] = 4;
                if(sendto(sfd, buf, 4, 0, (struct sockaddr*) & amp;sin, sizeof(sin)) < 0)
                {
                    ERR_MSG("sendto");
                    ret_val = -1;
                    break;
                }
 
                if(res-4 < 512)
                {
                    printf("======= file download completed =======\\
");
                    break;
                }
            }
        }
        else if(5 == buf[1])//error package
        {
            printf("MSG_ERR: code[%d] msg[%s] __%d__\\
", \
                    ntohs(*(unsigned short*)(buf + 2)), buf + 4, __LINE__);
            ret_val = -1;
            break;
        }
 
    }
 
    close(fd);
    return ret_val;
}

head.h file

#ifndef __HEAD_H__
#define __HEAD_H__


#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__:",__LINE__);\
perror(msg);\
}while(0)
#include <dirent.h> // directory entry
#include <fcntl.h> // file control
#include <fnmatch.h> // file name matching type
#include <glob.h> // path name pattern matching type
#include <grp.h> // group file
#include <netdb.h> // Network database operation
#include <pwd.h> // password file
#include <regex.h> // regular expression
#include <tar.h> // TAR archive values
#include <termios.h> // Terminal I/O
#include <unistd.h> // symbolic constants
#include <utime.h> // file time
#include <wordexp.h> // character extension type
#include <arpa/inet.h> // INTERNET definition
#include <net/if.h> // socket local interface
#include <netinet/in.h> // INTERNET address family
#include <netinet/tcp.h> // Transmission Control Protocol definition
#include <sys/mman.h> // memory management statement
#include <sys/select.h> // Select function
#include <sys/socket.h> // socket interface
#include <sys/stat.h> // file status
#include <sys/times.h> // process time
#include <sys/types.h> // basic system data types
#include <sys/un.h> // UNIX domain socket definition
#include <sys/utsname.h> // system name
#include <sys/wait.h> // process control

#include <cpio.h> // cpio archive value
#include <dlfcn.h> // dynamic link
#include <fmtmsg.h> // message display structure
#include <ftw.h> // file tree roaming
#include <iconv.h> // code set conversion usage program
#include <langinfo.h> // language information constants
#include <libgen.h> // pattern matching function definition
#include <monetary.h> // currency type
//#include <ndbm.h> // Database operation
#include <nl_types.h> // message categories
#include <poll.h> // Poll function
#include <search.h> // search table
#include <strings.h> // string operations
#include <syslog.h> // system error log record
#include <ucontext.h> // user context
#include <ulimit.h> // user limit
#include <utmpx.h> // user account database
#include <sys/ipc.h> // IPC (named pipe)
#include <sys/msg.h> // message queue
#include <sys/resource.h> // resource operation
#include <sys/sem.h> // semaphore
#include <sys/shm.h> // shared storage
#include <sys/statvfs.h> // file system information
#include <sys/time.h> // time type
#include <sys/timeb.h> // additional date and time definitions
#include <sys/uio.h> // vector I/O operation

#include <aio.h> // asynchronous I/O
#include <mqueue.h> // message queue
#include <pthread.h> // thread
#include <sched.h> // Execution Scheduling
#include <semaphore.h> // semaphore
#include <spawn.h> // real-time spawn interface
#include <stropts.h> // XSI STREAMS interface
//#include <trace.h> // Event trace

#include <assert.h> //Set the insertion point
#include <ctype.h> //Character processing
#include <errno.h> //Define error codes
#include <float.h> // Floating point processing
#include <iso646.h> //Macro corresponding to various operators
#include <limits.h> //Define the most valuable constants of various data types
#include <locale.h> //Define localized C functions
#include <math.h> //Definition of mathematical functions
#include <setjmp.h> //Exception handling support
#include <signal.h> //Signal mechanism support
#include <stdarg.h> //undefined parameter list support
#include <stddef.h> //Common constants
#include <stdio.h> //Define input/output functions
#include <stdlib.h> //Define miscellaneous functions and memory allocation functions
#include <string.h> //string processing
#include <time.h> //Define functions about time
#include <wchar.h> //Wide character processing and input/output
#include <wctype.h> // wide character classification
#endif
#include <head.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#define PORT 6666 //1024~49151
#define IP "192.168.122.15" //IP address, local IP ifconfig

struct cli_msg
{
    int newfd;
    struct sockaddr_in cin;
};

void* deal_cli_msg(void* arg);

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

    //Settings allow ports to be reused quickly
    int resume = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }

    //Fill the address information structure of the server
    //The real address information structure is executed according to the address family, AF_INET: man 7 ip
    struct sockaddr_in sin;
    sin.sin_family = AF_INET; //must fill in AF_INET;
    sin.sin_port = htons(PORT); //The network byte order of the port number, 1024~49151
    sin.sin_addr.s_addr = inet_addr(IP); //The network byte order of the IP address, ifconfig view

    // Binding --- must be bound
    if(bind(sfd, (struct sockaddr*) & amp;sin, sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("bind success");

    //Set the socket to passive listening state
    if(listen(sfd, 128) < 0)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("listen success");


    //Function: blocking function, blocking to wait for the client to connect successfully.
    //When the client connects successfully, it will get a client information from the queue head of the completed connection,
    //And generate a new file descriptor; the new file descriptor is the file descriptor that communicates with the client
    struct sockaddr_in cin; //store the address information of the successfully connected client
    socklen_t addrlen = sizeof(cin);
    int newfd = -1;

    pthread_t tid;
    struct cli_msg info;

    while(1)
    {
        //The main thread is responsible for the connection
        //Before the accept function blocks, it will preselect an unused file descriptor
        //When unblocking, it will judge whether the pre-selected file descriptor is used
        //If it is used, re-traverse an unused one
        //If not used, return the pre-file descriptor directly;
        newfd = accept(sfd, (struct sockaddr*) &cin, &addrlen);
        if(newfd < 0)
        {
            ERR_MSG("accept");
            return -1;
        }
        printf("[%s : %d] newfd=%d client successfully connected\\
", \
                inet_ntoa(cin. sin_addr), ntohs(cin. sin_port), newfd);

        info.newfd = newfd;
        info.cin = cin;

        //If it can run to the current position, it means that a client has successfully connected
        //You need to create a branch thread to interact with the client
        if(pthread_create( &tid, NULL, deal_cli_msg, &info) != 0)
        {
            fprintf(stderr, "pthread_create failed __%d__\\
", __LINE__);
            return -1;
        }
    
        pthread_detach(tid); //detach thread
    }


    //Close all socket file descriptors
    close(sfd);
    return 0;
}

//Thread execution body
void* deal_cli_msg(void* arg) //void* arg = (void*) & amp;info
{

    //---------problem------
    int newfd = ((struct cli_msg*)arg)->newfd;
    struct sockaddr_in cin = ((struct cli_msg*)arg)->cin;

    char buf[128] = "";
    ssize_t res = -1;
    while(1)
    {
        bzero(buf, sizeof(buf));
        //take over
        res = recv(newfd, buf, sizeof(buf), 0);
        if(res < 0)
        {
            ERR_MSG("recv");
            break;
        }
        else if(0 == res)
        {
            fprintf(stderr, "[%s : %d] newfd=%d client offline\\
", \
                    inet_ntoa(cin. sin_addr), ntohs(cin. sin_port), newfd);
            break;
        }
        printf("[%s : %d] newfd=%d : %s\\
", \
                inet_ntoa(cin. sin_addr), ntohs(cin. sin_port), newfd, buf);

        //Send -- concatenate the data into a *_* and send it back
        strcat(buf, "*_*");
        if(send(newfd, buf, sizeof(buf), 0) < 0)
        {
            ERR_MSG("send");
            break;
        }
        printf("send success\\
");
    }
    close(newfd);

    pthread_exit(NULL);
}

In the above procedure

1. Can the newfd in multi-thread be changed to be global, no, why?
2. Can the newfd of the branch thread not be saved in the multi-thread, and directly access the newfd of the main thread indirectly with a pointer, no, why?

//Must be saved separately, because threads under the same process share all resources of its subsidiary process
//If global is used, newfd and cin will be overwritten every time the client is connected
//If the pointer is used to access external member variables indirectly, it will also cause the member variables to be overwritten.