作业二、基于UDP的TFTP文件传输
#include <myhead.h>
#define SER_PORT 69
#define SER_IP "192.168.2.73"
// 菜单函数
void menu()
{
printf("1、***********下载文件*********\n");
printf("2、***********上传文件*********\n");
printf("3、***********退出*************\n");
}
// 下载函数
int down_file(int cfd, struct sockaddr_in sin, socklen_t len)
{
// 定义数组
char buf[516] = "";
printf("请输入要下载的文件名>>>");
char filename[128] = "";
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename) - 1] = 0;
// 发送下载请求
short *p1 = (short *)buf; // 操作码
*p1 = htons(1);
char *p2 = buf + 2; // 文件名
strcpy(p2, filename);
char *p3 = p2 + strlen(p2) + 1; // 模式位
strcpy(p3, "octet");
int size = 2 + strlen(p2) + strlen(p3) + 2; // 请求包总长度
if (sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("sendto error");
return -1;
}
// 打开或创建本地文件
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1)
{
perror("open error");
return -1;
}
while (1)
{
ssize_t res = recvfrom(cfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len);
short *p4 = (short *)buf;
if (ntohs(*p4) == 3)
{
write(fd, buf + 4, res - 4);
*p4 = htons(4);
sendto(cfd, buf, 4, 0, (struct sockaddr *)&sin, len);
if (res < 516)
{
printf("下载完成\n");
break;
}
}
}
close(fd);
}
// 上传函数
int up_file(int cfd, struct sockaddr_in sin, socklen_t len)
{
// 定义数组
char buf[516] = "";
printf("请输入要上传的文件名>>>");
char filename[128] = "";
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename) - 1] = 0;
// 发送上传申请
short *p1 = (short *)buf; // 操作码
*p1 = htons(2);
char *p2 = buf + 2; // 文件名
strcpy(p2, filename);
char *p3 = p2 + strlen(p2) + 1; // 模式位
strcpy(p3, "octet");
int size = 2 + strlen(p2) + strlen(p3) + 2; // 请求包总长度
sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sizeof(sin));
int fd = open(filename, O_RDONLY, 0644); // 本地创建
if (fd == -1)
{
perror("open error");
return -1;
}
// 定义块编码
short num = 1;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
while (1)
{
bzero(buf, sizeof(buf));
// 封装数据并发送给服务器
short *q1 = (short *)buf;
*q1 = htons(3);
short *q2 = p1 + 1;
*q2 = htons(num);
int res = read(fd, buf + 4, 512);
sendto(cfd, buf, res + 4, 0, (struct sockaddr *)&addr, sizeof(addr));
// 接收服务器发来的数据
memset(buf, 0, sizeof(buf));
recvfrom(cfd, buf, 4, 0, (struct sockaddr *)&addr, &addrlen);
// 判断发来的是ACK包并且编号正确
if (ntohs(*q2) == num && ntohs(*q1) == 4)
{
// 判断文件有没有读取完毕
if (res < 512)
{
printf("上传成功\n");
break;
}
num++; // 编号加1,然后进行下一次发送
}
else if (ntohs(*q2) != num && ntohs(*q1) == 4)
{
// 将光标移动到这一次读取之前
lseek(fd, -res, SEEK_CUR);
}
}
close(fd);
}
int main(int argc, const char *argv[])
{
// 1、创建用于通信的套接字文件描述符
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
// 将请求包发送给服务器
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
socklen_t len = sizeof(sin);
while (1)
{
system("clear");
menu();
printf("请输入您的选择:");
int num = 0;
scanf("%d", &num);
getchar();
switch (num)
{
case 1:
down_file(cfd, sin, len);
break;
case 2:
up_file(cfd, sin, len);
break;
case 3:
exit(1);
default:
printf("输入错误\n");
break;
}
printf("按任意键加回车清屏\n");
while (getchar() != '\n')
;
}
// 5、关闭套接字
close(cfd);
return 0;
}