tftp客户端下载
代码
int do_download(int cfd, struct sockaddr_in sin)
{
//发送下载请求
char buf[N] = "";
char filename[N - 10] = "";
printf("请输入需要下载的文件>>> ");
scanf("%s", filename);
while(getchar() != 10);
unsigned short *p1 = (unsigned short *)buf;
*p1 = htons(1);
char *p2 = buf + 2;
strcpy(p2, filename);
char *p3 = p2 + strlen(p2);
*p3 = 0;
char *p4 = p3 + 1;
strcpy(p4, MODE);
int size = 2 + strlen(p2) + 1 + strlen(p4) + 1;
if (sendto(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//打开文件,用于存储下载后的文件
int fd = -1; //必须初始化成一个无效的文件描述符
socklen_t addrlen = sizeof(sin);
ssize_t res = 0;
unsigned short num = 0; //记录本地的块编号
while (1)
{
bzero(buf, sizeof(buf));
//接收数据
res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &addrlen);
if (res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//printf("%d %d %d %d | %d %d\n", buf[0], buf[1], buf[2], buf[3], ntohs(*(short *)buf), ntohs(*(short *)(buf + 2)));
//由于操作码占两个字节,且是个大端字节序
//所以低字节存储在高地址,高字节存储在低地址
//所以有效操作码,存储在高地址上,即buf[1]的位置
if (3 == buf[1]) //数据包
{
//判断服务器返回的数据包的块编号与本地记录的块编号是否一致
if (*(unsigned short *)(buf + 2) == htons((num + 1)))
{
num++; //更新本地记录的块编号
if (-1 == fd)
{
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd < 0)
{
ERR_MSG("open");
return -1;
}
}
//将数据写入到文件中
if (write(fd, buf + 4, res - 4) < 0)
{
ERR_MSG("write");
close(fd);
return -1;
}
//发送ACK ----》 发送到临时端口
buf[1] = 4;
if (sendto(cfd, buf, 4, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//若接收到的数据小于512,则跳出循环,结束下载
if ((res - 4) < 512)
{
printf("---%s 文件下载完毕---\n", filename);
break;
}
}
}
else if (5 == buf[1]) //错误包
{
printf("---ERROR: %d %s---\n", ntohs(*(short *)(buf + 2)), buf + 4);
close(fd);
return -1;
}
}
close(fd);
return 0;
}
结果