tftp协议概述
tftp(简单文件传输协议)是基于UDP适用于网络上进行文件传输的标准协议。
特点:
应用层协议,基于UDP协议实现。
下载文件功能实现过程
1.服务器在69号端口等待客户端的请求。
2.客户端发送读写请求报文,服务器批准并使用临时接口与客户端进行通信。
3.服务器向客户端发送数据包并在数据包头设置块编码(可在客户端校验次序)。
4.客户端收到数据包后向服务器发送带有操作码和块编码的ACK
5.客户端校验收到的数据是否小于512byte,若小于则停止继续接收信息。
请求代码编写:
客户端的请求代码由2byte的操作码+未知大小的文件名+0(1byte)+数据传输模式+0(1byte)组成本次通过字符串完成请求代码的编写
//创建读请求包
unsigned char buf[516]="";
char *ptr = buf;
short int * pa =(short int *)ptr;
*pa = htons(1);//1为读2为写,并转化成网络字节序
char *pb=ptr+2;
strcpy(pb,filename);
char * pc= pb+strlen(pb);
char *pd=pc+1;
strcpy(pd,"octet");
收发功能编写
由ACK与数据包的前4byte可见两者的3~4byte都存放块编码,获取ACK只需将从服务器接受到的数据包取前4byte再将操作码改为4即可。
while (1)
{
bzero(buf,sizeof(buf));
//接收数据包
res = recvfrom(sfd,buf,516,0,(struct sockaddr*)&sin,&addrlen);
printf(" %ld ",n);
n+=res;
if (res<0)
{
ERR_MSG("recvfrom");
return -1;
}
write(fb,buf+4,res-4);
buf[1]=4; //将操作码改为4,pg的前四个字节就是ACK
if(sendto(sfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0)//发送ACK包
{
ERR_MSG("sendto");
return -1;
}
if (res<516)
{
break;
}
}
总代码
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_MSG(msg) do {\
fprintf(stderr,"_%d_",__LINE__);\
perror(msg);\
}while(0)
#define N 512
#define filename "5.png"
int main(int argc, const char *argv[])
{
if (argc<3)
{
printf("请输入端口号和IP\n");
return -1;
}
int port = atoi(argv[2]);
//创建报式套接字
int sfd =socket(AF_INET,SOCK_DGRAM,0);
if (sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("create socket success\n");
//填充地址信息结构体,真实的地址信息结构体与协议族有关
//AF_INEt 详见 man 7 ip
struct sockaddr_in sin;
sin.sin_family =AF_INET;
sin.sin_port =htons(port);//主机字节序转网络字节序的端口号
sin.sin_addr.s_addr = inet_addr(argv[1]);
//创建读请求包
unsigned char buf[516]="";
char *ptr = buf;
short int * pa =(short int *)ptr;
*pa = htons(1);//1为读2为写,并转化成网络字节序
char *pb=ptr+2;
strcpy(pb,filename);
char * pc= pb+strlen(pb);
char *pd=pc+1;
strcpy(pd,"octet");
size_t sz = 2+strlen(pb)+1+strlen("octet")+1; //数据包大小
//发送下载请求
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
int fb= open("./5.png",O_WRONLY|O_CREAT,0664);//创建一个文件将读取到的数据写入该文件
socklen_t addrlen= sizeof(sin);
ssize_t res=0;
long int n=0;
while (1)
{
bzero(buf,sizeof(buf));
//接收数据包
res = recvfrom(sfd,buf,516,0,(struct sockaddr*)&sin,&addrlen);
printf(" %ld ",n);
n+=res;
if (res<0)
{
ERR_MSG("recvfrom");
return -1;
}
write(fb,buf+4,res-4);
buf[1]=4; //将操作码改为4,pg的前四个字节就是ACK
if(sendto(sfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin))<0)//发送ACK包
{
ERR_MSG("sendto");
return -1;
}
if (res<516)
{
break;
}
}
close(sfd);
close(fb);
return 0;
}
测试结果
编译运行后通过eog图片可见图片下载成功。
在服务器中也显示传输完成