1>fttf协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:是应用层协议,基于UDP协议实现
数据传输模式:1、octet:二进制模式(常用)
2、mail:已经不再使用
#include<myhead.h>
#define SER_PORT 69 //服务器端口号
#define SER_IP "192.168.0.3" //服务器ip地址
//客户端可绑定可不绑定
//#define CLI_POPR 5555 //客户端端口号
//#define CLI_IP "192.168.111.187" //客户端ip地址
void menu() //菜单函数
{
printf("******************\n");
printf("******1.下载******\n");
printf("******2.上传******\n");
printf("******3.退出******\n");
printf("******************\n");
}
//下载
int text_add()
{
//创建套接字文件描述符
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
//填充地址信息
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
//下载数据
char name[128]="";
printf("请输入需要下载的文件名>>>");
scanf("%s",name);
//2向服务器下载请求
char buf[516]="";
short *p1=(short *)buf; //操作码
*p1=htons(1);
char *p2=buf + 2; //文件名 由自己输入
strcpy(p2,name);
char *p4=p2+strlen(p2)+1; //模式位 由用户输入
strcpy(p4,"octet");
int size=2+strlen(p2)+strlen(p4)+2;//请求包的总长度
socklen_t addrlen=sizeof(sin);
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("sendto 1 error");
return -1;
}
int fd=-1; //文件描述符
int res; //字符数
unsigned short num=1;
while(1)
{
//清空容器
bzero(buf,sizeof(buf));
res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
if(res==-1)
{
perror("recv error");
return -1;
}
if(3==buf[1])
{
//判断块编号和本地块编号
if(*(short *)(buf+2)==htons(num))
{
num++;
fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd==-1)
{
perror("open error");
return -1;
}
}
//写数据
write(fd,buf+4,res-4);
//发送ack包
buf[1]=4;
if(sendto(sfd, buf, 4, 0, (struct sockaddr*)&sin, sizeof(sin)) == -1)
{
perror("sendto error");
return -1;
}
if(res<516)
{
printf("下载成功\n");
break;
}
}
else if(buf[1]==5)
{
printf("error");
break;
}
}
//关闭套接字
close(sfd);
close(fd);
return 0;
}
//上传
int up_text()
{
//创建套接字文件描述符
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
//填充地址信息
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
//上传
char name[128]="";
printf("请输入需要上传的文件名>>>");
scanf("%s",name);
//打开文件 只读
int rfd=-1;
if((rfd=open(name,O_RDONLY))==-1)
{
perror("open error");
return -1;
}
//2向服务器上传
char buf[516]="";
short *p1=(short *)buf; //操作码
*p1=htons(2);
char *p2=buf + 2; //文件名 由自己输入
strcpy(p2,name);
char *p4=p2+strlen(p2)+1; //模式位 由用户输入
strcpy(p4,"octet");
int size=2+strlen(p2)+strlen(p4)+2;//请求包的总长度
socklen_t addrlen=sizeof(sin);
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("sendto error");
return -1;
}
int res=0; //字符数
unsigned short num=0;
while(1)
{
//清空容器
bzero(buf,sizeof(buf));
res=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);
if(res==-1)
{
perror("recv error");
return -1;
}
if(4==buf[1])
{
//判断块编号和本地块编号
if(num==ntohs(*(short *)(buf+2)))
{
buf[1]=3;
num++;
*(short*)(buf+2)=htons(num);
//读取数据
res=read(rfd,buf+4,sizeof(buf)-4);
if(res==0)
{
printf("上传成功\n");
break;
}
}
else
{
printf("上传失败\n");
break;
}
}
else if(5==buf[1])
{
perror("error");
break;
}
}
//关闭套接字
close(sfd);
close(rfd);
return 0;
}
/************************主程序**************************/
int main(int argc, const char *argv[])
{
int num=0;
while(num!=3)
{
menu(); //调用菜单函数
printf("请输入>>>");
scanf("%d",&num);
switch(num)
{
case 1: //下载
text_add(); //调用下载函数
break;
case 2: //上传
up_text(); //调用上传函数
break;
case 3: //退出
break;
default:
printf("输入错误,请重新输入!\n");
}
}
return 0;
}