二、基于UDP的TFTP文件传输
1)tftp协议概述
简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2)tftp下载模型
TFTP通信过程总结
- 服务器在69号端口等待客户端的请求
- 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
- 每个数据包的编号都有变化(从1开始)
- 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
- 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。
3)tftp协议分析
差错码:
0 未定义,差错错误信息
1 File not found.
2 Access violation.
3 Disk full or allocation exceeded.
4 illegal TFTP operation.
5 Unknown transfer ID.
6 File already exists.
7 No such user.
8 Unsupported option(s) requested.
#include <myhead.h>
#define SER_PORT 69 //服务器端口号
#define SER_IP "192.168.0.115" //服务器ip地址
#define CLI_PORT 5555 //客户端端口号
#define CLI_IP "192.168.0.100" //客户端ip地址
#define BUF_SIZE 516 //缓冲区大小
#define TIMEOUT 3 //超时时间(秒)
void display()
{printf("请选择功能:\n");printf("1. 文件下载\n");printf("2. 文件上传\n");printf("3. 退出程序\n");
}
int main(int argc, const char *argv[])
{while (1){int k = 0;display();int choice;scanf("%d", &choice);char filename[100];if (choice == 1 || choice == 2){printf("请输入文件名: ");scanf("%s", filename);}int file = open(filename, O_RDWR | O_CREAT, 0664);if (file == -1){printf("无法打开文件 %s\n", filename);return 1;}switch (choice){case 1:{//1、创建用于通信的服务器套接字文件描述符int cfd = socket(AF_INET, SOCK_DGRAM, 0);if (cfd == -1){perror("socket error");return -1;}printf("cfd = %d\n", cfd); //3//2、向服务器发送下载请求char buf[BUF_SIZE];short *p1 = (short *)buf;*p1 = htons(1); //操作码char *p2 = buf + 2;strcpy(p2, filename); //文件名char *p4 = p2 + strlen(p2) + 1;strcpy(p4, "octet"); //模式位int size = 2 + strlen(p2) + strlen(p4) + 2; //请求包的总长度//将请求包发送给服务器struct sockaddr_in sin;socklen_t sinlen = sizeof(sin);sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sinlen);while (1){ssize_t str_len = recvfrom(cfd, buf, BUF_SIZE, 0, (struct sockaddr *)&sin, &sinlen);if (ntohs(*p1) == 3){write(file, buf + 4, str_len - 4);*p1 = htons(4);sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sinlen);if (str_len < BUF_SIZE - 4){break;}}}//发送分手请求*p1 = htons(5);sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sinlen);close(cfd);bzero(buf, BUF_SIZE);break;}case 2:{// 1、创建用于通信的服务器套接字文件描述符int cfd = socket(AF_INET, SOCK_DGRAM, 0);if (cfd == -1){perror("socket error");return -1;}printf("cfd = %d\n", cfd); //3// 2、向服务器发送上传请求char buf[BUF_SIZE];short *p1 = (short *)buf;*p1 = htons(2); //操作码,表示上传char *p2 = buf + 2;strcpy(p2, filename); //文件名char *p4 = p2 + strlen(p2) + 1;strcpy(p4, "octet"); //模式位int size = 2 + strlen(p2) + strlen(p4) + 2; //请求包的总长度// 将请求包发送给服务器struct sockaddr_in sin;socklen_t sinlen = sizeof(sin);sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sinlen);short *ack = (short *)(buf + 2);int n = 1;while (1){ssize_t str_len = recvfrom(cfd, buf, BUF_SIZE, 0, (struct sockaddr *)&sin, &sinlen);if (ntohs(*p1) == 4){int res = read(file, buf + 4, BUF_SIZE - 4);*p1 = htons(3);*ack = htons(n);n++;sendto(cfd, buf, BUF_SIZE, 0, (struct sockaddr *)&sin, sinlen);if (res < BUF_SIZE - 4){break;}}}//发送分手请求*p1 = htons(5);sendto(cfd, buf, size, 0, (struct sockaddr *)&sin, sinlen);close(cfd);bzero(buf, BUF_SIZE);break;}case 3:{k = 1;break;}}printf("k=%d\n", k);if (k == 1){break;}}
}