基于UDP的TFTP文件传输
代码:
#include <myhead.h>
//实现下载功能
int download(int cfd,struct sockaddr_in sin)
{
char buf[516] = ""; //定义资源包
char fileName[128] = ""; //定义文件名
printf("请输入文件名:");
scanf("%s",fileName);
getchar();
//打开文件
int fd = -1;
if((fd = open(fileName,O_RDWR|O_CREAT|O_TRUNC|0666)) == -1)
{
perror("open error"); //读写形式创建文件,文件名为输入的文件名
return -1;
}
//组装请求包
short *p1 = (short *)buf;
*p1 = htons(1);
char *p2 = buf + 2;
strcpy(p2,fileName);
char *p3 = p2+strlen(p2)+1;
strcpy(p3,"octet");
int size = 4+strlen(p2)+strlen(p3); //整体长度
//先发送下载请求到服务器
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("sendto error");
return -1;
}
printf("请求成功!\n");
socklen_t socklen = sizeof(sin);
char *p5 = buf + 4; //确定数据的位置以及差错信息的位置
while(1)
{
bzero(buf,sizeof(buf));
int res=recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&socklen);
int A = buf[1]; //因为小端存储,操作码放在buf的第二位
if(A == 3) //当操作码为3时,证明是数据包,正常接收
{
write(fd,p5,sizeof(buf));
buf[1]=4; //将操作码改为4,即ACK
//将buf前四位发送回去,因为操作码已经修改,发回去四位当做ACK回复
sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin));
}
else if(A == 5) //当操作码为5时,证明为ERROR
{
printf("%s\n",p5);
return -1;
}
if(res < 516) //当接收到的字节数小于516时,证明已经接收完成,关闭循环
break;
}
printf("下载完成\n");
close(fd); //关闭文件
}
//实现上传功能
int upload(int cfd,struct sockaddr_in sin)
{
char buf[516] = ""; //定义资源包
char fileName[128] = ""; //定义文件名
printf("请输入文件名:");
scanf("%s",fileName);
getchar();
//打开文件
int fd = -1;
if((fd = open(fileName,O_RDONLY)) == -1)
{
perror("open error"); //只读形式打开文件,文件名为输入的文件名
return -1;
}
//组装请求包
short *p1 = (short *)buf;
*p1 = htons(2); //操作码为2代表要上传
char *p2 = buf + 2;
strcpy(p2,fileName);
char *p3 = p2+strlen(p2)+1;
strcpy(p3,"octet");
int size = 4+strlen(p2)+strlen(p3); //整体长度
//先发送上传请求到服务器
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("sendto error");
return -1;
}
printf("请求成功!\n");
socklen_t socklen = sizeof(sin);
int n = 1;
while(1)
{
bzero(buf,sizeof(buf));
recvfrom(cfd,buf,4,0,(struct sockaddr*)&sin,&socklen);
int res = read(fd,buf+4,sizeof(buf)-4); //将文件的512位写入数据包
buf[1]=3; //将操作码改为3,即数据包
//将buf发送,因为操作码已经修改,发回去数据加上了操作码和块编号
sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
if(res==0) //证明已经上传完成,关闭循环
break;
}
printf("上传完成\n");
close(fd); //关闭文件
}
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("input error");
return -1;
}
//创建套接字
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
//填充服务器结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr(argv[1]);
int key = 0;
while(1)
{
system("clear");
printf("\t\t菜单\t\t\n");
printf("\t\t1.下载\t\t\n");
printf("\t\t2.上传\t\t\n");
printf("\t\t0.关闭\t\t\n");
printf("请输入选项:");
scanf("%d",&key);
getchar();
switch(key)
{
case 1:
{
//下载
int A =download(cfd,sin);
};break;
case 2:
{
//上传
int B =upload(cfd,sin);
};break;
case 0:
{
goto A;
};break;
default:
printf("请输入正确的选项!\n");
}
//阻塞
printf("输入任意键,按回车清空");
while(getchar() != '\n');
}
A:
close(cfd);
return 0;
}
上传文件
下载文件