#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
//打印错误新的宏函数
#define ERR_MSG(msg) do{\
fprintf(stderr, " __%d__ ", __LINE__);\
perror(msg);\
}while(0)
#define N 600
int main(int argc, const char *argv[])
{
//检测是否有3个参数
if(argc < 3)
{
fprintf(stderr, "请输入IP filename\n");
return -1;
}
//创建套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if(cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器的IP地址以及端口号
struct sockaddr_in sin;
sin.sin_family = AF_INET; //IPV4
sin.sin_port = htons(69); //指定端口号转网络字节序
sin.sin_addr.s_addr = inet_addr(argv[1]); //指定ip地址转网络字节序
//保存目标地址信息结构体
struct sockaddr_in ser;
socklen_t addrlen=sizeof(ser);
//下载请求包
unsigned char buf[N]={0};
char* ptr=buf;
//操作码
short int*pa=(short int*)ptr;
*pa=htons(1);
//文件名
char* pb=ptr+2;
strcpy(pb,argv[2]);
//0
char* pc=pb+strlen(pb);
//模式
char* pd=pc+1;
strcpy(pd,"octet");
//长度
int size=2+strlen(pb)+1+strlen("octet")+1;
//向服务器发送下载请求
if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("sendto");
return -1;
}
//在本地创建用于保存下载内容的文件
int fd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0775);
if(fd<0)
{
ERR_MSG("open");
return -1;
}
//消息包编号
short count=1;
ssize_t res=0;
//循环接收并写入数据包
while(1)
{
bzero(buf,sizeof(buf));
//读取服务器发送的数据包
res=recvfrom(cfd,buf,516,0,(struct sockaddr*)&ser,&addrlen);
if(res<0)
{
ERR_MSG("recvfrom");
return -1;
}
//判断是否出错
if(ntohs(*pa)!=3)
{
fprintf(stderr,"%s",buf+4);
continue;
}
//判断数据包编号是否连续
if(ntohs(*(short*)pb)!=count)
{
continue;
}
//写入数据
write(fd,buf+4,res-4);
//判断是否下载所有数据
if(res<516)
{
printf("下载完成\n");
break;
}
//ACk包
*pa=htons(4);
*(short*)pb=htons(count++);
//发送ACK包
if(sendto(cfd,buf,4,0,(struct sockaddr*)&ser,addrlen)<0)
{
ERR_MSG("sendto");
return -1;
}
}
//关闭套接字
close(cfd);
//关闭文件描述符
close(fd);
return 0;
}
8.10作业
最新推荐文章于 2024-06-14 13:37:51 发布