网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)

网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)_第1张图片

 网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)_第2张图片

基于UDP的TFTP文件传输tftpd32

1)tftp协议概述

简单文件传输协议,适用于在网络上进行文件传输的一套标准协议,使用UDP传输

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)_第3张图片

2)tftp下载模型

网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)_第4张图片

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

3)tftp协议分析

网络编程 DAY2+基于UDP的TFTP文件传输tftpd32(云盘)_第5张图片

差错码:

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.

4)完整代码

#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;
        }
    }
}

你可能感兴趣的:(c语言)