1)tftp协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2)tftp下载模型
TFTP通信过程总结
- 服务器在69号端口等待客户端的请求
- 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
- 每个数据包的编号都有变化(从1开始)
- 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
- 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
3)tftp协议分析
差错码:
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<myhead.h>
#define SER_IP "192.168.125.107"
#define SER_PORT 69
#define CLI_IP "192.168.125.137"
#define CLI_PORT 7777
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
int fd=0;//标记文件描述符
ssize_t bytes;//用来表示读取到字节的个数
int load(char filename[],int cfd,struct sockaddr_in sin,socklen_t addrlen){
int num = 0;
int flag =0;//表示文件不存在
//===============下载逻辑=====================//
//发送下载请求
//拼接读写请求
char load_buf[516] = "";
//读写请求操作码
short *p1 =(short *) load_buf;
*p1 = htons(1);
//组装文件名
char *p2 = load_buf+2;
strcpy(p2,filename);
//组装模式
char *p3 = p2 + strlen(p2)+1;
strcpy(p3,"octet");
//发送请求包
int size = 2+ strlen(p2)+1+strlen(p3)+1;
sendto(cfd,load_buf,size,0,(struct sockaddr*)&sin,sizeof(sin));
while(1){
//如果接受数据小于0 perrori
bytes = recvfrom(cfd,load_buf,sizeof(load_buf),0,(struct sockaddr*)&sin,&addrlen);
if(bytes<0)
{
perror("recvfrom error");
return -1;
}
printf("BYTES:%ld\n",(long)bytes);
//如果操作马为5,则是错误马
if(load_buf[1]==5){
printf("错误信息:%s\n",load_buf+4);
return -1;
}else if(load_buf[1]==3){
//如果操作马是3,那么这个是返回的数据包
if (flag == 0)//如果文件不存在创建文件描述符
{//
fd = open("./5.png",O_CREAT | O_WRONLY | O_TRUNC,0777);
if(fd<0){
perror("open error");
return -1;
}
printf("fd:%d\n",fd);
//将文件标志为置为1,表示文件存在了
flag = 1;
}
//如果块编号为1,并且字节数为5166
if(((ntohs(*(unsigned short *)(load_buf+2))==num+1))&&(bytes==516)){
printf("fd*:%d\n",fd);
printf("bytes:%ld\n",bytes);
num = ntohs(*(unsigned short *)(load_buf+2));
int i=0;
if((i = write(fd,load_buf+4,bytes-4))<0){
printf("=====%d\n",i);
perror("write error1");
return -1;
}
//操作马设置为4 ack
load_buf[1] =4;
//发送ACK回复报文
if(sendto(cfd,load_buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0 ){
perror("send ack error");
return -1;
}
//如果bytes小于516直接写入
}else if(((ntohs(*(unsigned short *)(load_buf+2))==num+1))&&(bytes<516)) {
if(write(fd,load_buf+4,bytes-4) < 0){
printf("fd:%d\n",fd);
perror("write");
return -1;
}
load_buf[1]= 4;
if(sendto(cfd,load_buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0 ){
perror("send ack error");
return -1;
}
printf("下载完成\n");
return -1;
}
}
} close(fd);
}
int upload(char filename[],int cfd,struct sockaddr_in sin,socklen_t addrlen){
//拼接请求报文
//拼接操作马
printf("------\n");
int num = 0;//快编号
char upload_buf[516] = "";
short *p1 =(short *) upload_buf;
*p1 = htons(2);
//凭借文件名
char *p2 = upload_buf + 2;
strcpy(p2,filename);
//拼接传输模式
char *p3 = p2 + strlen(p2) +1;
strcpy(p3,"octet");
int size = 2+ strlen(p2)+1+strlen(p3)+1;
if(sendto(cfd,upload_buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("sendto error");
return -1;
}
int fd = open(filename,O_RDONLY);
if(fd < 0){
perror("open error");
return -1;
}
printf("文件存在\n");
while(1){
bytes = recvfrom(cfd,upload_buf,sizeof(upload_buf),0,(struct sockaddr*)&sin,&addrlen);
printf("bytes:%ld\n",bytes);
if(bytes <0){
perror("recvfrom error");
return -1;
}
if(bytes == 4){
//长度是4是应答报文
if(upload_buf[1]==4){
printf("====%d\n",*(unsigned short*)(upload_buf+2));
if(htons(num) == *(unsigned short*)(upload_buf+2)){
num++;
printf("ack码正确\n");
}else {
printf("ack玛错误\n");
}
}else if(upload_buf[1]==5){
printf("操作玛为5,错误\n");
return -1;
}
}
//发送数据
bzero(upload_buf+4,512);
ssize_t rsize = read(fd,upload_buf+4,512);
upload_buf[1] = 3;
*(unsigned short *)(upload_buf+2) = htons(num);
if(sendto(cfd,upload_buf,rsize+4,0,(struct sockaddr*)&sin,sizeof(sin))==-1){
perror("sendto error");
return -1;
}
if(rsize<512){
break;}
}
printf("文件上传成功\n");
close(fd);
return 0;
}
int main(int argc, const char *argv[])
{
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd == -1){
perror("socket error");
return -1;
}
printf("socket success\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);
if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))==-1){
perror("bind error");
return -1;
}
printf("bind success\n");
struct sockaddr_in sin;
socklen_t addrlen = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
printf("******************\n");
printf("******1.下载******\n");
printf("******2.上传******\n");
printf("******3.推出******\n");
printf("******************\n");
char filename[128] ="";
int sig;
scanf("%d",&sig);
switch(sig){
case 1://执行下载
printf("请输入要下载的文件\n");
// fgets(filename,sizeof(filename),stdin);
// filename[(strlen(filename)-1)] = 0;
scanf("%s",filename);
printf("%s",filename);
load(filename,cfd,sin,addrlen);
break;
case 2://执行上传
printf("请输入要上传的文件名:\n");
scanf("%s",filename);
printf("%s\n",filename);
upload(filename,cfd,sin,addrlen);
break;
case 3://执行退出
exit(0);
}
close(cfd);
return 0;
}