简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输
特点:
是应用层协议
基于UDP协议实现
数据传输模式
octet:二进制模式(常用)
mail:已经不再支持
2)tftp下载模型
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
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 69
#define IP "10.102.52.180"
#define BUFFER_SIZE 512
// 定义TFTP的操作码
#define RRQ 1
#define WRQ 2
#define DATA 3
#define ACK 4
#define ERRID 5
void handle_rrq(int sockfd, struct sockaddr_in server_addr, char *filename);
void handle_wrq(int sockfd, struct sockaddr_in server_addr, char *filename);
int main() {
// 创建一个UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(-1);
}
// 定义服务器的地址结构体
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
char choice;
char filename[32];
while (1) {
printf("请输入下载或上传(0:下载 1:上传): ");
scanf(" %c", &choice);
printf("请输入文件名: ");
scanf("%s", filename);
if (choice == '0') {
handle_rrq(sockfd, server_addr, filename);
} else if (choice == '1') {
handle_wrq(sockfd, server_addr, filename);
} else {
printf("无效选择,请重新输入。\n");
}
}
close(sockfd);
return 0;
}
// 处理读请求的函数
void handle_rrq(int sockfd, struct sockaddr_in server_addr, char *filename) {
char buffer[BUFFER_SIZE + 4];
int nbytes;
unsigned short block_num = 0;
unsigned short opcode;
struct sockaddr_in response_addr;
socklen_t addr_len = sizeof(response_addr);
// 构建RRQ请求
*(unsigned short *)buffer = htons(RRQ);
sprintf(buffer + 2, "%s%c%s%c", filename, 0, "octet", 0);
nbytes = 2 + strlen(filename) + 1 + 5 + 1; // 2 bytes opcode, 1 for each zero byte, 5 bytes "octet"
sendto(sockfd, buffer, nbytes, 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 打开或创建文件,准备写入数据
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (fd == -1) {
perror("打开文件失败");
exit(-1);
}
while (1) {
nbytes = recvfrom(sockfd, buffer, BUFFER_SIZE + 4, 0, (struct sockaddr*)&response_addr, &addr_len);
if (nbytes == -1) {
perror("接收文件数据失败");
exit(-1);
}
opcode = ntohs(*(unsigned short *)buffer);
block_num = ntohs(*(unsigned short *)(buffer + 2));
if (opcode == DATA) {
write(fd, buffer + 4, nbytes - 4);
if (nbytes < 516) { // 512 bytes data数据 + 4 bytes TFTP 头
printf("文件 [%s] 下载成功。\n", filename);
close(fd);
break;
}
// 发送确认ACK给服务器
*(unsigned short *)buffer = htons(ACK);
*(unsigned short *)(buffer + 2) = htons(block_num);
sendto(sockfd, buffer, 4, 0, (struct sockaddr*)&response_addr, addr_len);
} else if (opcode == ERRID) {
printf("服务器报告错误: %s\n", buffer + 4);
close(fd);
remove(filename); // 删除不完整的文件
break;
}
}
}
// 处理写请求的函数
void handle_wrq(int sockfd, struct sockaddr_in server_addr, char *filename) {
char buffer[BUFFER_SIZE + 4];
int nbytes;
unsigned short block_num = 0;
unsigned short opcode;
struct sockaddr_in response_addr;
socklen_t addr_len = sizeof(response_addr);
// 构建WRQ请求
*(unsigned short *)buffer = htons(WRQ);
sprintf(buffer + 2, "%s%c%s%c", filename, 0, "octet", 0);
nbytes = 2 + strlen(filename) + 1 + 5 + 1;
sendto(sockfd, buffer, nbytes, 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 打开文件,准备读取数据
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
exit(-1);
}
while (1) {
// 等待服务器的ACK确认
nbytes = recvfrom(sockfd, buffer, BUFFER_SIZE + 4, 0, (struct sockaddr*)&response_addr, &addr_len);
opcode = ntohs(*(unsigned short *)buffer);
if (opcode == ACK) {
if (ntohs(*(unsigned short *)(buffer + 2)) != block_num) {
printf("未从服务器接收到期望的ACK确认。\n");
close(fd);
break;
}
block_num++; // 块号递增
nbytes = read(fd, buffer + 4, BUFFER_SIZE);
if (nbytes == -1) {
perror("读取文件数据失败");
close(fd);
exit(-1);
}
*(unsigned short *)buffer = htons(DATA);
*(unsigned short *)(buffer + 2) = htons(block_num);
sendto(sockfd, buffer, nbytes + 4, 0, (struct sockaddr*)&response_addr, addr_len);
if (nbytes < BUFFER_SIZE) {
printf("文件 [%s] 上传成功。\n", filename);
close(fd);
break;
}
} else if (opcode == ERRID) {
printf("服务器报告上传错误:%s\n", buffer + 4);
close(fd);
break;
}
}
}