TFTP客户端开源代码分析
·
TFTP客户端开源代码分析
本文件详细分析TFTP客户端的实现,包括数据结构、层次关系和工作流程。
1. 文件结构
client.c- 客户端主应用逻辑文件client_comm.c- 通信模块的实现文件client_comm.h- 通信模块的头文件tftpx.h- 通用TFTP协议定义
2. 数据结构
2.1 struct tftpx_packet (tftpx.h)
struct tftpx_packet {
uint16_t cmd; // 操作码
uint16_t block; // 块编号
char data[512]; // 数据内容
};
- cmd: 操作码,取值为RRQ(1)、WRQ(2)、DATA(3)、ACK(4)、ERROR(5)
- block: 数据块编号,从1开始递增
- data: 数据内容,最大512字节
2.2 struct sockaddr_in (client_comm.c)
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
inet_pton(AF_INET, server_ip, &(server.sin_addr.s_addr));
- sin_family: 地址族,固定为AF_INET(IPv4)
- sin_port: 端口号,使用htons()转换为网络字节序
- sin_addr: IP地址,使用inet_pton()转换为二进制格式
3. 层次关系
+-------------------+
| client.c |
+-------------------+
| 业务逻辑层 |
| - 命令解析 |
| - 流程控制 |
+-------------------+
|
v
+-------------------+
| client_comm.c |
+-------------------+
| 通信层 |
| - 套接字操作 |
| - 数据包发送 |
| - 数据包接收 |
+-------------------+
3.1 层次说明
- client.c: 业务逻辑层,负责命令解析和流程控制
- client_comm.c: 通信层,实现底层套接字操作,向上提供封装好的接口
4. 初始化流程
4.1 命令行参数解析 (client.c:main)
if(argc < 3){
printf("Usage: %s <server> <port> <get|put> <file>\n", argv[0]);
return 0;
}
const char *server_ip = argv[1];
unsigned short port = atoi(argv[2]);
const char *cmd = argv[3];
const char *file = argv[4];
4.2 套接字初始化 (client_comm.c:client_comm_init)
int client_comm_init(const char *server_ip, unsigned short port) {
addr_len = sizeof(struct sockaddr_in);
sender_len = sizeof(struct sockaddr_in);
memset(&sender, 0, sizeof(struct sockaddr_in));
if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
printf("Server socket could not be created.\n");
return -1;
}
// 初始化服务器地址
server.sin_family = AF_INET;
server.sin_port = htons(port);
inet_pton(AF_INET, server_ip, &(server.sin_addr.s_addr));
return 0;
}
4.3 流程分支 (client.c:main)
if(strcmp(cmd, "get") == 0){
do_get(server_ip, port, file);
} else if(strcmp(cmd, "put") == 0){
do_put(server_ip, port, file);
} else {
printf("Unknown command: %s\n", cmd);
return 0;
}
5. 发送文件流程(PUT操作)
5.1 准备WRQ数据包 (client.c:do_put)
struct tftpx_packet snd_packet;
snd_packet.cmd = htons(CMD_WRQ);
sprintf(snd_packet.data, "%s\0%s\0", file, "octet");
5.2 发送WRQ数据包 (client.c:do_put)
client_comm_send(&snd_packet, sizeof(struct tftpx_packet));
5.3 等待ACK响应 (client.c:do_put)
struct tftpx_packet rcv_packet;
int r_size = client_comm_recvfrom(&rcv_packet, sizeof(struct tftpx_packet));
if(r_size >= 4 && rcv_packet.cmd == htons(CMD_ACK)){
printf("Received ACK for block 0\n");
}
5.4 分块发送文件数据 (client.c:do_put)
FILE *fp = fopen(file, "rb");
if(fp == NULL){
printf("Can't open file: %s\n", file);
return;
}
char buffer[512];
int block = 1;
while(!feof(fp)){
int bytes_read = fread(buffer, 1, 512, fp);
struct tftpx_packet data_packet;
data_packet.cmd = htons(CMD_DATA);
data_packet.block = htons(block);
memcpy(data_packet.data, buffer, bytes_read);
if(client_comm_send(&data_packet, bytes_read + 4) == -1){
printf("Error sending block %d\n", block);
break;
}
// 等待ACK
int ack_received = 0;
while(!ack_received){
struct tftpx_packet ack_packet;
int r_size = client_comm_recvfrom(&ack_packet, sizeof(struct tftpx_packet));
if(r_size >= 4 && ack_packet.cmd == htons(CMD_ACK) && ack_packet.block == htons(block)){
ack_received = 1;
}
}
block++;
}
fclose(fp);
6. 接收文件流程(GET操作)
6.1 准备RRQ数据包 (client.c:do_get)
struct tftpx_packet snd_packet;
snd_packet.cmd = htons(CMD_RRQ);
sprintf(snd_packet.data, "%s\0%s\0", file, "octet");
6.2 发送RRQ数据包 (client.c:do_get)
client_comm_send(&snd_packet, sizeof(struct tftpx_packet));
6.3 接收DATA数据包 (client.c:do_get)
FILE *fp = fopen(file, "wb");
if(fp == NULL){
printf("Can't create file: %s\n", file);
return;
}
struct tftpx_packet rcv_packet;
int block = 1;
while(1){
int r_size = client_comm_recvfrom(&rcv_packet, sizeof(struct tftpx_packet));
if(r_size < 4){
printf("Error receiving data\n");
break;
}
if(rcv_packet.cmd == htons(CMD_DATA) && ntohs(rcv_packet.block) == block){
// 写入文件
fwrite(rcv_packet.data, 1, r_size - 4, fp);
// 发送ACK
struct tftpx_packet ack_packet;
ack_packet.cmd = htons(CMD_ACK);
ack_packet.block = rcv_packet.block;
client_comm_send_to(&ack_packet, sizeof(struct tftpx_packet));
// 检查是否为最后一个块
if(r_size - 4 < 512){
printf("Received final block\n");
break;
}
block++;
}
}
fclose(fp);
7. client_comm.c中的关键函数
- 7.1 client_comm_init - 初始化套接字通信
- 7.2 client_comm_send - 向服务器发送数据
- 7.3 client_comm_send_to - 向特定地址发送数据
- 7.4 client_comm_recvfrom - 从服务器接收数据
- 7.5 client_comm_close - 关闭套接字连接
- 7.6 client_comm_get_sock - 获取套接字描述符
- 7.7 client_comm_get_server - 获取服务器地址
- 7.8 client_comm_get_addr_len - 获取地址长度
8. client.c中的关键函数
- 8.1 main - 主入口点,解析参数并启动操作
- 8.2 do_get - 处理文件下载(RRQ)
- 8.3 do_put - 处理文件上传(WRQ)
- 8.4 parse_opts - 解析命令行选项
9. TFTP协议细节
- 9.1 使用UDP协议进行通信
- 9.2 默认端口:69
- 9.3 支持RRQ(读请求)和WRQ(写请求)
- 9.4 数据以512字节的块传输
- 9.5 每个块由接收方确认
- 9.6 最后一个块的大小小于512字节
10. 错误处理
- 10.1 数据包传输的超时处理
- 10.2 丢失数据包的重传机制
- 10.3 处理服务器返回的错误数据包
- 10.4 向用户报告连接错误
11. 完整源码
下载链接:https://download.csdn.net/download/wxmtwfx/9272566
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)