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
- The server waits for the client’s request on port 69
- If the server approves the request, it will use the ephemeral port to communicate with the client.
- The number of each packet changes (starts from 1)
- Each data packet must be confirmed by ACK. If a timeout occurs, the last data packet or ACK packet needs to be resent
- 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.