思维导图
练习题
1.基于UDP的TFTP文件传输,实现文件下载上传
cpp
#include <myhead.h>
int main(int argc, char const *argv[])
{
// 创建套接字UDP通信
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sockfd)
{
perror("socket error");
return -1;
}
// 填充服务器网络信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
//端口号填69
sin.sin_port = htons(69);
//填写windows ip地址
sin.sin_addr.s_addr = inet_addr("192.168.124.19");
//结构体长度
socklen_t socklen = sizeof(sin);
//数据包数据的长度以512字节传输
char buffer[516] = {0};
//返回的ACK
char ack[4];
//操作码
short code = 0;
//块编号 or 错误码
short BlockoErr = 0;
//数据的长度以512Byte传输
char text[512] = {0}; //文件内容 或 错误信息
//校验收到的块编号
int N = 0;
//返回的文件描述符
int fd;
int ret = 0;
//输入文件名
char filename[64] = {0};
printf("下载文件名: ");
scanf("%s", filename);
//使用 sprintf 组包
//返回成功格式化字符的个数
ret = sprintf(buffer, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0);
//首次发送请求
if (-1 == sendto(sockfd, buffer, ret, 0, (struct sockaddr *)&sin, socklen))
{
perror("sendto error");
return -1;
}
//循环接收服务器发来的数据包
while (1)
{
//因为有临时端口需要保存服务器的网络信息结构体
ret = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&sin, &socklen);
if (-1 == ret)
{
perror("recvfrom error");
return -1;
}
//解析操作码
code = ntohs(*(short *)buffer);
//解析块编号or错误码
BlockoErr = ntohs(*(short *)(buffer + 2));
//解析文件内容or错误信息,并将内容放入数据段
strncpy(text, buffer + 4, sizeof(text));
//解析数据包中的内容
if (3 == code && BlockoErr == N + 1)
{
//校验块编号+1
N++;
//要接收的数据包
//如果是第一次接到数据包 要创建文件
if (BlockoErr == 1)
{
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (-1 == fd)
{
perror("open error");
}
}
//将文件内容写入文件 ret是buffer中去掉前4字节操作数和块编号所剩余的内容
if (-1 == write(fd, text, ret - 4))
{
perror("write error");
}
// 组装ACK
*(short *)ack = htons(4);
*(short *)(ack + 2) = htons(BlockoErr);
//回复ACK包
if (-1 == sendto(sockfd, ack, 4, 0, (struct sockaddr *)&sin, socklen))
{
perror("recvfrom error");
}
//文件接收完毕
if (ret < 512)
break;
}
}
printf("文件[%s]下载完成\n", filename);
close(sockfd);
return 0;
}