#include #include #include #include #include #include #include #include #include #include /* * ./a.out server_ip 下载的文件名 * ./tftpc server_ip filename */ int main(int argc, char const *argv[]) { if (argc < 3) { printf("usage: %s server_ip filename\n", argv[0]); return 1; } int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) { perror("socket"); return -1; } // 生成请求报文(数据包) char request[64]; sprintf(request, "%c%c%s%c%s%c", 0, 1, argv[2], 0, "octet", 0); // 发送请求 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(69); // TFTP 端口号 server_addr.sin_addr.s_addr = inet_addr(argv[1]); size_t request_size = strlen(argv[2]) + 9; sendto(sock_fd, request, request_size, 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); // 创建本地文件的描述符(打开或创建文件) int file_fd = open(argv[2], O_CREAT | O_WRONLY, 0666); while (1) { char buf[516] = ""; struct sockaddr_in data_addr; // bzero(&data_addr, sizeof(data_addr)); socklen_t data_addr_len = sizeof(data_addr); ssize_t recv_len = recvfrom(sock_fd, buf, 516, 0, (struct sockaddr *)&data_addr, &data_addr_len); // 判断接收的数据是否 OK if (buf[1] == 3) { printf("ok: %d -> %s\n", *(buf + 3), buf + 4); // 收到的是数据包 write(file_fd, buf + 4, recv_len - 4); // 回 ACK "Acknowledgment"(确认) buf[1] = 4; sendto(sock_fd, buf, 4, 0, (struct sockaddr *)&data_addr, data_addr_len); } else if (buf[1] == 5) { // 收到的是错误信息包 printf("error: %d -> %s\n", *(buf + 3), buf + 4); execlp("rm", "rm", "-rf", argv[2], NULL); } else if (buf[1] == 6) { // 收到的是 OACK 包,包含请求选项回传的值 } if (recv_len < 516) { printf("数据接收完成\n"); break; // 接到最后一组数据后不再接收 } } close(file_fd); close(sock_fd); return 0; }