TFTP模型
TFTP通信过程
- 服务器在69号端口等待客户端的请求
- 服务器若批准客户端的请求,则使用临时端口与客户端进行通信
- 每个数据包的编号都有变化
- 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的包(数据包或ACK)
- 数据的长度为512byte,小于512byte的数据意味着传输结束
TFTP协议
数据传输模式
octet:二进制模式(常用)
netascii:文本模式
TFTP服务器
TFTP客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_LOG(msg) do{\
perror(msg);\
printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define PORT 69
#define N 516
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("请输入服务端的IP\n");
return 0;
}
//1、创建数据报式socket套接字
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_LOG("socket");
return -1;
}
//2、填充服务端 IP & PORT
struct sockaddr_in saddr;//dest_addr
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t saddr_len = sizeof(saddr);
int option;
char buf[N] = "";
char filename[N] = "";
char pathname[N] = "./";
int size;
int fd;
unsigned short num = 0;//数据报编号
int recv_len;//数据报大小
while(1)
{
saddr.sin_port = htons(PORT);
printf("****************\n");
printf("*****1.下载*****\n");
printf("*****2.上传*****\n");
printf("*****3.退出*****\n");
printf("****************\n");
printf("请输入选项--->");
scanf("%d",&option);
getchar();
switch(option)
{
case 1://下载
//输入文件名
bzero(filename,N);
strcpy(pathname,"./");
printf("请输入要下载的文件名--->");
fgets(filename,N,stdin);
filename[strlen(filename)-1] = 0;
//封装读请求数据报
bzero(buf,N);
size = sprintf(buf,"%c%c%s%c%s%c",0,1,filename,0,"octet",0);
//发送下载请求数据报
if(sendto(sfd,buf,size,0,(struct sockaddr*)&saddr,saddr_len) < 0)
{
ERR_LOG("sendto");
break;
}
strcat(pathname,filename);
fd = open(pathname,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd < 0)
{
ERR_LOG("open");
break;
}
num = 0;//数据报编号
recv_len = 0;//数据报大小
while(1)
{
//接收数据报
bzero(buf,N);
recv_len = recvfrom(sfd,buf,N,0,(struct sockaddr*)&saddr,&saddr_len);
if(recv_len < 0)
{
ERR_LOG("recvfrom");
break;
}
//对数据报的处理
if(3 == buf[1])//DATA
{
if(num+1 == ntohs(*(unsigned short*)(buf+2)))
{
//写入数据到文件
if(write(fd,buf+4,recv_len-4) < 0)
{
ERR_LOG("write");
break;
}
//发送应答ACK(num)
buf[1] = 4;
if(sendto(sfd,buf,4,0,(struct sockaddr*)&saddr,saddr_len) < 0)
{
ERR_LOG("sendto");
break;
}
//下载完毕退出
if(recv_len < N)
{
printf("****下载完毕****\n");
close(fd);
break;
}
num++;
}
}
else if(5 == buf[1])//ERROR
{
printf("ERROR:%s\n",buf+4);
break;
}
}
break;
case 2://上传
//输入文件名
bzero(filename,N);
strcpy(pathname,"./");
printf("请输入要上传的文件名--->");
fgets(filename,N,stdin);
filename[strlen(filename)-1] = 0;
//封装写请求数据报
bzero(buf,N);
size = sprintf(buf,"%c%c%s%c%s%c",0,2,filename,0,"octet",0);
//发送上传请求数据报
if(sendto(sfd,buf,size,0,(struct sockaddr*)&saddr,saddr_len) < 0)
{
ERR_LOG("sendto");
break;
}
strcat(pathname,filename);
fd = open(pathname,O_RDONLY);
if(fd < 0)
{
ERR_LOG("open");
break;
}
num = 0;//数据报编号
recv_len = 0;//数据报大小
while(1)
{
//接收到ACK
bzero(buf,N);
recv_len = recvfrom(sfd,buf,N,0,(struct sockaddr*)&saddr,&saddr_len);
if(recv_len < 0)
{
ERR_LOG("recvfrom");
break;
}
//发送数据报
if(num == ntohs(*(unsigned short*)(buf+2)))
{
//从文件读取数据
bzero(buf+4,N);
int data_len = read(fd,buf+4,512);
if(data_len < 0)
{
ERR_LOG("read");
break;
}
//上传完毕退出
else if(0 == data_len)
{
printf("****上传完毕****\n");
close(fd);
break;
}
else
num++;
//封装发送数据报
buf[1] = 3;
//sprintf(buf+2,"%u",htons(num));
//buf[3] = num;
*(unsigned short*)(buf+2) = htons(num);
if(sendto(sfd,buf,4+data_len,0,(struct sockaddr*)&saddr,saddr_len) < 0)
{
ERR_LOG("sendto");
break;
}
//printf("%d\n",num);
//printf("htons:%x\n",htons(num));
}
}
break;
case 3://退出
//close文件描述符
close(sfd);
exit(1);
break;
default:
printf("请输入正确的选项!");
break;
}
printf("请输入任意字符清屏>>>");
while(getchar()!=10);
system("clear");
}
close(sfd);
return 0;
}
结果
本文链接:https://shengto.top/network_programming/udp_TFTP.html
转载时须注明出处及本声明