數據傳輸模式: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");}//綁定客戶端端口與IPstruct 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){//判斷服務器是否發來ACKif(*(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;}}
}