主函数:
#include "head.h"
typedef unsigned short ushort;
int main(int argc, const char *argv[])
{//创建套接字int sfd = socket(AF_INET,SOCK_DGRAM,0);if(sfd < -1){ERR_MSG("socket");return -1;}//填充服务器地址信息struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.s_addr = inet_addr(IP);socklen_t addrlen = sizeof(sin);int flag = -1;while(1){printf("选择操作(1:下载;2:上传;3:退出\n)");scanf("%d",&flag);while(getchar()!=10);if(1 == flag)DownLoad(sfd,addrlen,sin);else if(2 == flag)updata(sfd,addrlen,sin);else if(3 == flag)break;}close (sfd);return 0;
}
库函数:
#include "head.h"
void DownLoad(int sfd,socklen_t addrlen,struct sockaddr_in sin)
{char data[516] = "";ssize_t dataSize = -1;//打开文件并获得流指针printf("输入文件名:\n");fgets(data,sizeof(data),stdin);data[strlen(data)-1] = 0;FILE*fp = fopen(data,"w+");if(NULL == fp){ERR_MSG("fopen");exit(-1);}bzero(data,sizeof(data));do{size_t size = ReqBag(data,1);if(sendto(sfd,(void*)data,size,0,(struct sockaddr*)&sin,addrlen) < 0){ERR_MSG("sendto");exit(-1);}dataSize = recvfrom(sfd,(void*)data,sizeof(data),0,(struct sockaddr*)&sin,&addrlen); if(dataSize < 0){ERR_MSG("recvfrom");exit(-1);}if(*(short *)data == htons(3))break;else if(*(short*)data == htons(5)){puts(data+4);}}while(1);unsigned short num = -1;do{if(htons(num+1) == *(unsigned short*)(data + 2)){fwrite((void*)(data+4),1,dataSize-4,fp);}ACKbag(sfd,addrlen,sin,++num);if(dataSize < 516){printf("下载完成\n");fclose(fp);return ;}dataSize = recvfrom(sfd,(void*)data,sizeof(data),0,(struct sockaddr*)&sin,&addrlen); if(dataSize < 0){ERR_MSG("recvfrom");exit(-1);}if(*(short *)data == 5){puts(data+4);}}while(1);}
void updata(int sfd,socklen_t addrlen,struct sockaddr_in sin)
{char data[516] = "",ackbuf[4] = "";ssize_t dataSize = -1;unsigned short num = 0;ssize_t res = -1;//打开文件并获得流指针printf("输入文件名:\n");fgets(data,sizeof(data),stdin);data[strlen(data)-1] = 0;FILE*fp = fopen(data,"r");if(NULL == fp){ERR_MSG("fopen");exit(-1);}bzero(data,sizeof(data));ReqBag(data,2);//封请求包do{if(sendto(sfd,data,516,0,(struct sockaddr*)&sin,addrlen) < 0)//发送请求包,只要前面字段符合协议服务器就认{ERR_MSG("sendto");exit(-1);}res = recvfrom(sfd,data,sizeof(data),0,(struct sockaddr*)&sin,&addrlen);//收回包if(res < 0){ERR_MSG("recvfrom");exit(-1);}if(insp_recdata(data,num))//验回包,无误退出循环,有误继续上一步发送操作{num++;bzero(data,sizeof(data));break;}}while(1);//发送请求包并检验收到的ACKwhile(1)//发送数据包{res = getData(fp,data);//从文件中读取数据if(res == 0){printf("上传完成\n");fclose(fp);break;}while(1)//发送读取到的数据并检验回包,回包出错则重新发送上一个数据包并再次检验{sDataBag(sfd,addrlen,sin,num,data);//发送数据包,num:块编号res = recvfrom(sfd,ackbuf,sizeof(ackbuf),0,(struct sockaddr*)&sin,&addrlen);if(res < 0){ERR_MSG("recvfrom");exit(-1);}//收回包if(insp_recdata(ackbuf,num))//验证回包{bzero(data,sizeof(data));num++;break;}}}}
long ReqBag(char *data,char rw)//封装请求包
{char buf[128];printf("输入目标文件文件名\n");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;int size = sprintf(data,"%c%c%s%c%s%c",0,rw,buf,0,"octet",0);return (long)size;
}
//下载专用
void ACKbag(int sfd,socklen_t addrlen,struct sockaddr_in sin,unsigned short num)//回包
{char buf[4] = "";*(unsigned short*)buf = htons(4);*(unsigned short*)(buf+2) = htons(num);sendto(sfd,(void*)buf,4,0,(struct sockaddr*)&sin,addrlen);
}
///上传专用///
int insp_recdata(char data[],unsigned short num)//验证回包数据,无误返回1,重复返回0,错误直接退出进程
{if(*(unsigned short *)data == htons(5)){puts(data+4);printf("%d\n",__LINE__);exit(-1);}else if(*(unsigned short*)(data+2) == htons(num)){return 1;}elsereturn 0;
}size_t getData(FILE*fp,char data[])//从文件中获取数据并存入数据包数据段中
{size_t res = fread(data+4,1,512,fp) ;if(res < 0){ERR_MSG("fread");exit(-1);}return res;
}void sDataBag(int sfd,socklen_t addrlen,struct sockaddr_in sin,unsigned short num,char data[])//封装包头段并发送数据
{//封装包头段*(unsigned short*)data = htons(3);*(unsigned short*)(data+2) = htons(num);//发送数据,发送失败直接退进程if(sendto(sfd,data,516,0,(struct sockaddr*)&sin,addrlen) < 0){ERR_MSG("sendto");exit(-1);}
}
头文件:
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include<stdlib.h>
#define ERR_MSG(msg) do{\fprintf(stderr,"line:%d",__LINE__);\perror(msg);\
}while(0)
#define PORT 69 //1024~49151
#define IP "192.168.31.164"
void DownLoad(int sfd,socklen_t addrlen,struct sockaddr_in sin);
long ReqBag(char *data,char rw);void updata(int sfd,socklen_t addrlen,struct sockaddr_in sin);
void ACKbag(int sfd,socklen_t addrlen,struct sockaddr_in sin,unsigned short num);
int insp_recdata(char data[],unsigned short num);
size_t getData(FILE*fp,char data[]);
void sDataBag(int sfd,socklen_t addrlen,struct sockaddr_in sin,unsigned short num,char data[]);
#endif
流程图: