代码实现
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
//打印错误新的宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
if(argc < 2)
{
fprintf(stderr, "请输入IP port\n");
return -1;
}
//将获取到的端口号字符串,转换成整形
//int port = atoi(argv[2]);
//if(port < 1024 || port > 49151)
// {
// fprintf(stderr, "port %d input error!! 1024~49151\n", port);
// return -1;
// }
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success\n");
//绑定客户端自身的地址信息结构体 ---> 非必须绑定
//填充服务器的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(69);
sin.sin_addr.s_addr = inet_addr(argv[1]);
struct sockaddr_in rcv_addrmsg; //存储接收到的数据包来自哪里
socklen_t addrlen = sizeof(rcv_addrmsg);
char filename[128]={0};
printf("输入需要下载的文件名字>>>");
scanf("%s",filename);
//getchar();
printf("录入名字成功\n");
char buf[600] = "";
char str[512] = "";
char *ptr=buf;
short int*pa=(short int*)ptr;
*pa=htons(1);
char *pb=(char*)(pa+1);
strcpy(pb,filename);
char *pc=pb+strlen(pb);
char *pd=pc + 1;
strcpy(pd,"octet");
size_t size = 2+strlen(pb)+1+strlen("octet")+1;
printf("_____\n");
//发送请求
if(sendto(sfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return-1;
}
printf("sendto success\n");
//定义已经接收到的块编号
unsigned short block_count = 0;
int res1,res2,res3;
while(1)
{
bzero(buf,sizeof(buf));
//申请成功,接临时端口号进行通信
int res=0;
printf("**************\n");
if(-1==(res=recvfrom(sfd,buf,600,0,(struct sockaddr *)&rcv_addrmsg,&addrlen)))//res接收接收到的字节数
{
ERR_MSG("recvfrom");
return -1;
}
printf("**************\n");
//网络字节序---》主机字节序
int num = ntohs(*(unsigned short*)buf);//获取接收到的数据包中的操作码
int block_code = ntohs(*(unsigned short*)(buf+2));//获取接收到的数据包中的块编号
bzero(str,sizeof(str));
//strcpy(str,(buf+4));//获取接收到的数据包中的数据内容
switch(num)
{
case 3 :
{
if(block_code==block_count+1)//判断发送的包是否正确
block_count=block_code;
if(block_code==1)//当第一个数据包发送过来时
{
res1=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0777);//打开需要用来接收传输的文件,没有则创建
if(res1==-1)
{
ERR_MSG("open");
return -1;
}
}
res2=write(res1,buf+4,res-4);//将数据包中的数据内容写入文件
if(res2==-1)
{
ERR_MSG("write");
return -1;
}
//当完成接收的时候给服务器发送ACK
bzero(buf,sizeof(buf));//清空内容
*(unsigned short*)buf = htons(4);//存入操作码
*(unsigned short*)(buf+2) = htons(block_code);//存入块编号
//发送ACK
res3=sendto(sfd,buf,4,0,(struct sockaddr*)&rcv_addrmsg,addrlen);
if(res3==-1)
{
ERR_MSG("sendto");
return-1;
}
if(res<516)//判断当最后一次接收,数据内容字节数小于512时退出
{
return -1;
}
}
break;
case 5 :
{
printf("num=%d str=%s\n",num,str);
exit(0);
}
break;
}
}
//关闭套接字
close(sfd);
return 0;
}
运行现象