菜鸟笔记
提升您的技术认知

TFTP协议(基于UDP)

一、TFTP简介

TFTP:简单文件传送协议,最初用于引导无盘系统,被设计用来传输小文件

特点:

    基于UDP协议实现

    不进行用户有效性认证

数据传输模式:

    octet:二进制模式

    netascii:文本模式

    mail:已经不再支持

二、TFTP通信过程


1、服务器在69号端口等待客户端的请求

2、服务器若批准此请求,则使用临时端口与客户端进行通信

3、每个数据包的编号都有变化(从1开始)

4、每个数据包都要得到ACK的确认如果出现超时,则需要重新发送最后的包(数据或ACK)

5、数据的长度以512Byte传输

6、小于512Byte的数据意味着传输结束

三、协议分析


3.错误码
0 未定义,参见错误信息

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 <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

	if(argc!=3){

		printf("./a.out server_ip file_name\n");
		return 0;
	}

	//创建socket套接字(基于UDP)
	int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(socket_fd<0){

		perror("socket");
		return 0;
	}

	//组tftp文件读取请求
	unsigned char cmd[128]="";
	int len = sprintf(cmd,"%c%c%s%c%s%c",0x00,0x01,argv[2],0,"octet",0);

	//将请求cmd发送到服务器69端口
	struct sockaddr_in server;
	bzero(&server,sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(69);
	inet_pton(AF_INET,argv[1],&server.sin_addr.s_addr);

	sendto(socket_fd,cmd,len,0,(struct sockaddr *)&server,sizeof(server));

	//打开一个本地空文件,将得到的数据写进去
	int fd=open(argv[2],O_WRONLY | O_CREAT,0666);
	if(fd<0){

		perror("open");
		return 0;
	}

	//不停读取服务器传过来的文件数据
	unsigned short num=0;

	while(1){

		unsigned char buf[1024]="";
		struct sockaddr_in from;
		socklen_t from_len = sizeof(from);
		int len = recvfrom(socket_fd,buf,sizeof(buf),0,(struct sockaddr *)&from, &from_len);

		//判断收到的数据的操作码(必须是00 03表示文件数据)
		if(buf[1]==0x03){

			//将文件数据写入本地文件中
			//防止写入重复数据
			if((num+1) == ntohs(*(unsigned short *)(buf+2)))
			{

				write(fd,buf+4,len-4);
				num = ntohs(*(unsigned short *)(buf+2));
				printf("recv:%d\n",num);
			}

			//给服务器发送ACK回应
			buf[1]=4;
			sendto(socket_fd,buf,4,0,(struct sockaddr *)&from,sizeof(from));

			if(len<516){
     //最后一个文件数据

				break;
			}
		}

	}

	//关闭套接字
	close(socket_fd);
	//关闭文件
	close(fd);

	return 0; 

}