数据传输模式:octet(二进制模式)
#include<head.h>
char* down_up_request(char* buf,char* filename,int rw,int sockfd,struct sockaddr_in in);
int download(struct sockaddr_in in,char* filename,char* buf,int sockfd);
int upload(struct sockaddr_in in,char* filename,char* buf,int sockfd);
int main(int argc, const char *argv[])
{
/***********************************
基于UDF的TFTP文件传输,实现网盘功能
***********************************/
/**********客户端代码**************/
//创建套接文件
int rtsocket=socket(AF_INET,SOCK_DGRAM,0);
if(rtsocket==-1){
perror("socket");
return -1;
}
else{
printf("套接文件创建成功\n");
}
//绑定客户端端口与IP
struct sockaddr_in cin;
char* cip="192.168.176.130";
uint16_t cport=8888;
cin.sin_family=AF_INET;
cin.sin_port=htons(cport);
cin.sin_addr.s_addr=inet_addr(cip);
int rtbind=bind(rtsocket,(struct sockaddr*)&cin,sizeof(cin));
if(rtbind==0){
printf("与客户端绑定成功\n");
}
else if(rtbind==-1){
perror("bind");
return -1;
}
//数据收发
/*填充服务器地址与端口信息*/
struct sockaddr_in sin;
char* sip="192.168.118.161";
uint16_t sport=69;
sin.sin_family=AF_INET;
sin.sin_port=htons(sport);
sin.sin_addr.s_addr=inet_addr(sip);
/*********************************/
char data[516]={0};
char filename[128]={0};
printf("*******1<download>*******\n");
printf("*******2<upload>*********\n");
printf("*******3<exit>***********\n");
while(1){
int option=0;
printf("please choose option:");
scanf("%d",&option);
if(option<1&&option>3){
printf("enter error,please reenter\n");
scanf("%d",&option);
}
switch(option){
case 1 :
{
printf("please enter download filename:");
scanf("%s",filename);
getchar();
char* rt=down_up_request(data,filename,1,rtsocket,sin);
download(sin,filename,rt,rtsocket);
}
break;
case 2:
{
printf("please enter upload filename:");
scanf("%s",filename);
getchar();
char* rt=down_up_request(data,filename,2,rtsocket,sin);
upload(sin,filename,rt,rtsocket);
}
break;
case 3:
{
goto END;
}
break;
}
}
END:
close(rtsocket);
return 0;
}
//下载或上传请求
char* down_up_request(char* buf,char* filename,int rw,int sockfd,struct sockaddr_in in){
short* p1=(short*)buf;
*p1=htons(rw);//rw操作码转为网络字节序
char* p2=buf+2;
strcpy(p2,filename);//存入要下载的文件名
char* p3=p2+strlen(p2)+1;
strcpy(p3,"octet");//设置操作模式
int size=2+sizeof(p2)+1+sizeof(p3)+1;
ssize_t rtsendto=sendto(sockfd,buf,size,0,(struct sockaddr*)&in,sizeof(in));
if(rtsendto==-1){
perror("sendto");
return NULL;
}
else{
printf("发送请求成功\n");
}
return buf;
}
//下载
int download(struct sockaddr_in in,char* filename,char* buf,int sockfd){
int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
if(fd==-1){
perror("open");
return -1;
}
short num=0;
socklen_t addrlen=sizeof(in);
while(1){
bzero(buf,sizeof(buf));
ssize_t rtrecvfrom=recvfrom(sockfd,buf,516,0,(struct sockaddr*)&in,&addrlen);//循环接收数据
if(rtrecvfrom==-1){
perror("recvfrom");
return -1;
}
else{
printf("reading......\n");
}
if(buf[1]==3){//判断是否是数据包
if(*(short*)(buf+2)==htons(num+1)){//确认块编号接发是否一致
num++;
if(rtrecvfrom-4==512){//数据包后512字节为数据
ssize_t rtwrite=write(fd,buf+4,rtrecvfrom-4);
if(rtwrite<0){
printf("write error\n");
break;
}
}
char ACK[4]={0};
short *p=(short*)ACK;
*p=htons(4);
short *p1=(short*)(ACK+2);
*p1=htons(num);
ssize_t rtsendto=sendto(sockfd,ACK,4,0,(struct sockaddr*)&in,addrlen);//写入成功,向服务器发送ACK,确认
if(rtsendto==-1){
perror("sendto");
return -1;
}
if(rtrecvfrom<516){//读取小于516说明已经读取结束
ssize_t rtwrite=write(fd,buf+4,rtrecvfrom-4);
if(rtwrite<0){
printf("write error\n");
break;
}
printf("end of download\n");
close(fd);
break;
}
}
}
else if(buf[1]==5){ //错误信息
printf("error:%s\n",buf+4);
close(fd);
return -1;
}
}
}
//上传
int upload(struct sockaddr_in in,char* filename,char* buf,int sockfd){
int fd=open(filename,O_RDONLY);
if(fd==-1){
printf("file don't exist\n");
return -1;
}
short num=0;
socklen_t addrlen=sizeof(in);
while(1){
bzero(buf,sizeof(buf));
ssize_t rtrecvfrom=recvfrom(sockfd,buf,4,0,(struct sockaddr*)&in,&addrlen);//循环接收服务器确认消息
if(rtrecvfrom==-1){
perror("recvfrom");
return -1;
}
else{
printf("ready upload....\n");
}
// printf("ACK=%d\n",ntohs(*(short*)(buf+2)));//查看第一次发过来的ack
// 解析服务器数据,读取并发送数据包给服务器
if(buf[1]==4){//判断服务器是否发来ACK
if(*(short*)(buf+2)==htons(num)){//确认块编号接发是否一致,从0开始
ssize_t rtread=read(fd,buf+4,512);
printf("uploading.......\n");
// printf("rtread=%ld\n",rtread);
short *p=(short*)buf;
*p=htons(3);//修改为数据包,服务器识别数据包下载数据
num++;//ACK块编码确认+1给服务器确认
short *p1=(short*)(buf+2);
*p1=htons(num);
ssize_t rtsendto=sendto(sockfd,buf,rtread+4,0,(struct sockaddr*)&in,addrlen);//将上传的文件以每512字节发送给服务器,并发送ACK确认
if(rtsendto==-1){
perror("sendto");
return -1;
}
if(rtread<512){//读取小于512说明上传的文件已经读取完毕
printf("upload success\n");
close(fd);
break;
}
}
}
else if(buf[1]==5){ //错误信息
printf("error:%s\n",buf+4);
close(fd);
return -1;
}
}
}