创建tftp客户端
运行结果:
tftp协议
tftp下载模型
代码:
#include
#define PORT 69 //tftp服务器端口
#define IP "192.168.170.131" //tftp服务器运行环境的IP
int do_download(int cfd, struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);
int main(int argc, const char *argv[])
{
//创建报式套接字
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd < 0)
{
ERR_MSG("socket");
return -1;
}
printf("socket create success cfd = %d __%d__\n", cfd, __LINE__);
//填充服务器的地址信息结构体,AT_INET: man 7 ip
struct sockaddr_in sin;
sin.sin_family = AF_INET; //必须为AF_INET
sin.sin_port = htons(PORT); //端口号的网络字节序
sin.sin_addr.s_addr = inet_addr(IP); //服务器的网络字节序
char choose = 0;
while (1)
{
printf("-----------------\n");
printf("-----1. 下载-----\n");
printf("-----2. 上传-----\n");
printf("-----3. 退出-----\n");
printf("-----------------\n");
printf("请输入>>>");
choose = getchar();
while (getchar() != '\n')
; //循环吸收垃圾字符
switch (choose)
{
case '1':
// 下载
do_download(cfd, sin);
break;
case '2':
// 上传
do_upload(cfd, sin);
break;
case '3':
// 退出
exit(0);
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}
//关闭所有文件描述符
if (close(cfd) < 0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
// 下载
int do_download(int cfd, struct sockaddr_in sin)
{
//组下载请求包
char buf[516] = "";
char filename[20] = "";
printf("请输入要下载的文件名>>>");
scanf("%s", filename);
getchar();
//ptr1指向操作码
short *ptr1 = (short *)buf;
*ptr1 = htons(1);
char *ptr2 = (char *)(ptr1 + 1);
strcpy(ptr2, filename);
char *ptr3 = ptr2 + strlen(ptr2) + 1;
strcpy(ptr3, "octet");
size_t len = 2 + strlen(ptr2) + 1 + strlen(ptr3) + 1;
//接收数据的字节数
ssize_t rlen = 0;
//组ACK包
char ack_buf[4] = "";
short *ptr4 = (short *)ack_buf;
*ptr4 = htons(4);
short *ptr5;
//发送下载请求给tftp服务器 sendto
if (sendto(cfd, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
//打开要下载的文件,没有就创建文件
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0777);
if (fd < 0)
{
ERR_MSG("open");
return -1;
}
//接收服务器的IP和端口
struct sockaddr_in rcvadd;
int rcvlen;
while (1)
{
bzero(buf, sizeof(buf));
//接收服务器传回来的数据包 recvfrom
rlen = recvfrom(cfd, buf, 516, 0, (struct sockaddr *)&rcvadd, &rcvlen);
if (rlen < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//如果是ERROR包
if (*ptr1 == htons(5))
{
fprintf(stderr, "错误码:%d, %s\n", ntohs(*(ptr1 + 1)), (char *)(ptr1 + 2));
return -1;
}
//将收到的数据写入文件
if (write(fd, buf + 4, rlen - 4) < 0)
{
ERR_MSG("write");
return -1;
}
//组ACK包
ptr5 = (short *)(buf + 2);
*(ptr4 + 1) = *ptr5;
//回发应答包到tftp服务器 sendto
if (sendto(cfd, ack_buf, 4, 0, (struct sockaddr *)&rcvadd, rcvlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
if (rlen < 516)
{
printf("下载完毕\n");
break;
}
}
// 关闭文件描述符
if (close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
}
// 上传
int do_upload(int cfd, struct sockaddr_in sin)
{
//组上传请求包
char buf[516] = "";
char filename[20] = "";
// 块编号
short kid = 1;
printf("请输入要上传的文件名>>>");
scanf("%s", filename);
getchar();
//ptr1指向操作码
short *ptr1 = (short *)buf;
*ptr1 = htons(2);
char *ptr2 = (char *)(ptr1 + 1);
strcpy(ptr2, filename);
char *ptr3 = ptr2 + strlen(ptr2) + 1;
strcpy(ptr3, "octet");
size_t len = 2 + strlen(ptr2) + 1 + strlen(ptr3) + 1;
//读取文件要发送数据的字节数
ssize_t slen = 0;
// ACK包
char ack_buf[128] = "";
// 指向操作码
short *ptr4 = (short *)ack_buf;
// *ptr4 = htons(4);
//发送上传请求给tftp服务器 sendto
if (sendto(cfd, buf, len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
ERR_MSG("sendto");
return -1;
}
bzero(buf, sizeof(buf));
sleep(1);
//打开要上传的文件
int fd = open(filename, O_RDONLY);
if (fd < 0)
{
ERR_MSG("open");
return -1;
}
//接收要发送的服务器的IP和端口
struct sockaddr_in sndadd;
int sndlen;
int res;
while (1)
{
//接收服务器传回来的ACK包 recvfrom
res = recvfrom(cfd, ack_buf, 128, 0, (struct sockaddr *)&sndadd, &sndlen);
if (res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//如果是ERROR包
if (*ptr4 == htons(5))
{
fprintf(stderr, "错误码:%d, %s\n", ntohs(*(ptr4 + 1)), (char *)(ptr4 + 2));
return -1;
}
// 读取ACK包
if (*ptr4 == htons(4))
{
if (kid != ntohs(*(ptr4 + 1)) + 1)
{
// 发送上一次的数据包
if (sendto(cfd, buf, 4 + slen, 0, (struct sockaddr *)&sndadd, sndlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
continue;
}
}
// 组数据包
*ptr1 = htons(3);
*(ptr1 + 1) = htons(kid);
// 从文件中读取数据
slen = read(fd, buf + 4, 512);
if (slen < 0)
{
ERR_MSG("read");
return -1;
}
//发送数据包到tftp服务器 sendto
if (sendto(cfd, buf, 4 + slen, 0, (struct sockaddr *)&sndadd, sndlen) < 0)
{
ERR_MSG("sendto");
return -1;
}
kid++;
if (slen < 512)
{
printf("上传完毕\n");
break;
}
}
// 最后还有一个ACK应答包
res = recvfrom(cfd, ack_buf, 128, 0, (struct sockaddr *)&sndadd, &sndlen);
if (res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
// 关闭文件描述符
if (close(fd) < 0)
{
ERR_MSG("close");
return -1;
}
}