1.基于UDP的TFTP文件传输
1)tftp协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2)tftp下载模型
TFTP通信过程总结
服务器在69号端口等待客户端的请求
服务器若批准此请求,则使用 临时端口 与客户端进行通信。
每个数据包的编号都有变化(从1开始)
每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
3)tftp协议分析
4)代码
main.c
#include "tftp.h"
int main(int argc, char const *argv[])
{
while (1)
{
// 菜单
printf("*******************\n");
printf("******1:下载*******\n");
printf("******2:上传*******\n");
printf("******0:退出*******\n");
printf("*******************\n");
int a = -1;
printf("请输入要进行的功能:");
scanf("%d", &a);
switch (a)
{
case 1:
{
// 调用下载函数
if (download() == -1)
{
perror("下载失败");
return -1;
}
printf("请输入任意字符后按回车键继续...");
getchar(); // 等待用户输入一个字符
system("clear"); // 清空终端(在Linux系统中)
}
break;
case 2:
{
// 调用上传函数
if (upload() == -1)
{
perror("上传失败");
return -1;
}
printf("请输入任意字符后按回车键继续...");
getchar(); // 等待用户输入一个字符
system("clear"); // 清空终端(在Linux系统中)
}
break;
case 0:
{
goto END;
}
break;
default:
printf("请重新输入\n");
break;
}
}
END:
return 0;
}
tfitp.c
#include "tftp.h"
//定义下载文件函数
int download()
{
// 创建套接字文件
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
//printf("cfd = %d\n", cfd); // 3
// 向服务器发送下载请求
char buf[516] = "";
short *p1 = (short *)buf; // 操作码
*p1 = htons(1);
char *p2 = buf + 2; // 文件名
printf("请输入要下载的文件名:");
scanf("%s", p2);
getchar();
char *p4 = p2 + strlen(p2) + 1;
strcpy(p4, "octet");
int size = 2 + strlen(p2) + strlen(p4) + 2; // 请求包总长度
// 将请求包发送给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin));
printf("请求包发送成功\n");
// 绑定客户端
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(CLI_PORT);
cin.sin_addr.s_addr = inet_addr(CLI_IP);
// 打开要写入的文件
int fd = -1;
if ((fd = open(p2, O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
{
perror("open error");
return -1;
}
// 接收数据包
char msg[516] = "";
socklen_t len = sizeof(sin);
while (1)
{
int n = recvfrom(cfd, msg, sizeof(msg), 0, (struct sockaddr *)&sin, &len);
printf("%d\n",n);
if (n==-1)
{
perror("recvfrom error");
return -1;
}
short *p5 = (short *)msg; // 操作码
short *p6 = (short *)(msg + 2); // 块编号
char *p7 = msg + 4; // 文件内容
short num = ntohs(*p5);
short key = ntohs(*p6);
if (num == 3)
{
// 处理数据包
write(fd, p7, n-4);
// 发送ACK
char ack[516] = "";
short *p8 = (short *)ack; // 操作码
*p8 = htons(4);
short *p9 = (short *)(ack + 2); // 块编号
*p9 = htons(key);
sendto(cfd, ack, 4, 0, (struct sockaddr *)&sin, sizeof(sin));
if (n< 516)
{
printf("文件下载完毕\n");
break;
}
}
else if (num == 5)
{
printf("error\n");
break;
}
}
// 关闭文件描述符
close(cfd);
close(fd);
}
//定义上传文件函数
int upload()
{
// 创建套接字文件
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
printf("cfd = %d\n", cfd); // 3
// 向服务器发送上传请求
char buf[516] = "";
short *p1 = (short *)buf; // 操作码
*p1 = htons(2);
char *p2 = buf + 2; // 文件名
printf("请输入要上传的文件名:");
scanf("%s", p2);
getchar();
char *p4 = p2 + strlen(p2) + 1;
strcpy(p4, "octet");
int size = 2 + strlen(p2) + strlen(p4) + 2; // 请求包总长度
// 将请求包发送给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
socklen_t len = sizeof(sin);//数据包大小
sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, len);
printf("请求包发送成功\n");
// 绑定客户端
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(CLI_PORT);
cin.sin_addr.s_addr = inet_addr(CLI_IP);
// 打开要上传的文件
int fd = -1;
if ((fd = open(p2, O_RDONLY)) == -1)
{
perror("open error");
return -1;
}
short n = 0;
while (1)
{
// 等待ACK
if (recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len) == -1)
{
perror("接收ACK失败");
close(fd);
return -1;
}
// 准备一个要发送的数据包
char msg[516] = "";
short *p5 = (short *)msg; // 操作码
*p5 = htons(3);
short *p6 = (short *)(msg + 2); // 块编号
*p6 = htons(++n);
char *p7 = msg + 4; // 文件内容
read(fd, p7, 512);
int size_msg = 4 + strlen(p7);
// 发送数据包
sendto(cfd, msg, size_msg, 0, (struct sockaddr *)&sin, len);
printf("发送成功\n");
// 接收ACK
char ack[516] = "";
short *p8 = (short *)ack; // 操作码
short *p9 = (short *)(ack + 2); // 块编号
recvfrom(cfd, ack, 4, 0, (struct sockaddr *)&sin, &len);
// 判断是否上传完毕
if (strlen(p7) < 512)
{
printf("文件上传完毕\n");
break;
}
}
close(cfd);
close(fd);
return 0;
}
tftp.h
#ifndef TFTP_H
#define TFTP_H
#define SER_PORT 69 // 服务器端口号
#define SER_IP "192.168.0.131" // 服务器ip地址
#define CLI_PORT 5555 // 客户端端口号
#define CLI_IP "192.168.0.105" // 客户端地址
#include<myhead.h>
//定义下载文件函数
int download();
//定义上传文件函数
int upload();
#endif