我先研究udp 传输的机制,tftp是用udp 设计的一个不错应用。
在网上查找tftp 源代码,发现 https://github.com/ideawu/tftpx 上的源码比较好看,这个只是linux 下的代码。
在ubuntu 上make 了一下,就编译好了,然后测试程序,能按tftp 的方式运行。
那个链接包含服务端和客户端代码,我这里只是分析其客户端代码,因为就算客户端代码也很大的,相比其他的代码,这个还是算简单的。
这个客户端代码包含3个文件,
tftpx.h 定义tftp中的常量,与服务端程序共享。
client.h 就是几个函数的全局定义。
client.c 这个是主程序。
在查看 tftpx 的源码之前, 你最好先阅读 W.Richard.Stevens 的 TCP/IP Illustrated Volume 1: The Protocols(TCP/IP详解 卷1:协议).
tftpx 使用这样的代码来实现停止等待机制:
int send_packet(int sock, struct tftpx_packet *packet, int size){
struct tftpx_packet rcv_packet;
int time_wait_ack = 0;
int rxmt = 0;
int r_size = 0;
for(rxmt = 0; rxmt < PKT_MAX_RXMT; rxmt ++){
printf("Send block=%d\n", ntohs(packet->block));
if(send(sock, packet, size, 0) != size){
return -1;
}
for(time_wait_ack = 0; time_wait_ack < PKT_RCV_TIMEOUT; time_wait_ack += 20000){
usleep(20000);
// Try receive(Nonblock receive).
r_size = recv(sock, &rcv_packet, sizeof(struct tftpx_packet), MSG_DONTWAIT);
if(r_size >= 4 && rcv_packet.cmd == htons(CMD_ACK) && rcv_packet.block == packet->block){
//printf("ACK: block=%d\n", ntohs(rcv_packet.block));
// Valid ACK
break;
}
}
if(time_wait_ack < PKT_RCV_TIMEOUT){
break;
}else{
// Retransmission.
continue;
}
}
if(rxmt == PKT_MAX_RXMT){
// send timeout
printf("Sent packet exceeded PKT_MAX_RXMT.\n");
return -1;
}
return size;
}
tftp 的速度问题
从这个停止等待机制看,tftp 速度应该有点慢。
网上搜索tftp 速度,结果都说慢,都怀疑网络问题,从这个机制我们可以评估速度。因为usleep(20000)需要20ms 才能传一个包,一个包大小一般512字节,理想情况下 512x50=25600 字节/秒 50=1000/20
所以传送速度就是25k 字节/秒
这个usleep(20000) 可能有点不得已,每个包都要核实好了,才能下一个包,可能比ftp 都会慢。
要提高速度可以加大包的尺寸,减少usleep的数据。
或者采用多个包才核实等传送机制,如果就tftp 可能没办法。
主要的几个函数
void do_list(int sock, char *dir); 实现目录列表
void do_get(char *remote_file, char *local_file); 从服务端获取文件的实现
void do_put(char *filename); 发送文件到服务端
int main(int argc, char **argv) 这个是主函数
命令行分析,必须输入服务端的ip 地址, 端口号是可选的,如果与他的服务程序通讯,就是缺省为:
#define SERVER_PORT 10220
在建立通讯sock前,显示应用帮助文件
建立一个sock ,没有通讯测试一样
然后等待你输入tftp 命令,解析命令,然后调用上面3个函数,完成功能。
支持的tftp命令是 list,get,put,blocksize,quit
可以看帮助文件的函数实现:
void help(){
printf("Usage: cmd arg0[,arg1,arg2...]\n");
printf(" -Directory listing:\n");
printf(" list path\n");
printf(" -Download a file from the server:\n");
printf(" get remote_file[ local_file]\n");
printf(" -Upl