我先研究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 ACKbreak;}}if(time_wait_ack < PKT_RCV_TIMEOUT){break;}else{// Retransmission.continue;}}if(rxmt == PKT_MAX_RXMT){// send timeoutprintf("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("  -Upload a file to the server:\n");printf("    put filename\n");printf("  -Set blocksize:\n");printf("    blocksize size\n");printf("  -Quit this programm:\n");printf("    quit\n");
}

client.c 主程序

/*********************************************** Author: ideawu(www.ideawu.net)* Date: 2007-06* File: client.c*********************************************/#include "client.h"// Socket fd this client use.
int sock;
// Server address.
struct sockaddr_in server;
socklen_t addr_len;
int blocksize = DATA_SIZE;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("  -Upload a file to the server:\n");printf("    put filename\n");printf("  -Set blocksize:\n");printf("    blocksize size\n");printf("  -Quit this programm:\n");printf("    quit\n");
}int main(int argc, char **argv){char cmd_line[LINE_BUF_SIZE];char *buf;char *arg;int i;char *local_file;int done = 0; // Server exit.char *server_ip;unsigned short port = SERVER_PORT;addr_len = sizeof(struct sockaddr_in);   if(argc < 2){printf("Usage: %s server_ip [server_port]\n", argv[0]);printf("    server_port - default 10220\n");return 0;}help();server_ip = argv[1];if(argc > 2){port = (unsigned short)atoi(argv[2]);}printf("Connect to server at %s:%d", server_ip, port);if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){printf("Server socket could not be created.\n");return 0;}// Initialize server addressserver.sin_family = AF_INET;server.sin_port = htons(port);inet_pton(AF_INET, server_ip, &(server.sin_addr.s_addr));// Command line interface.while(1){printf(">> ");memset(cmd_line, 0, LINE_BUF_SIZE);buf = fgets(cmd_line, LINE_BUF_SIZE, stdin);if(buf == NULL){printf("\nBye.\n");return 0;}arg = strtok (buf, " \t\n");if(arg == NULL){continue;}if(strcmp(arg, "list") == 0){arg = strtok (NULL, " \t\n");if(arg == NULL){printf("Error: missing arguments\n");}else{do_list(sock, arg);}}else if(strcmp(arg, "get") == 0){arg = strtok (NULL, " \t\n");local_file = strtok (NULL, " \t\n");if(arg == NULL){printf("Error: missing arguments\n");}else{if(local_file == NULL){local_file = arg;}do_get(arg, local_file);}}else if(strcmp(arg, "put") == 0){arg = strtok (NULL, " \t\n");if(arg == NULL){printf("Error: missing arguments\n");}else{do_put(arg);}}else if(strcmp(arg, "blocksize") == 0){arg = strtok (NULL, " \t\n");if(arg == NULL){printf("Error: missing arguments\n");}else{int blk = atoi(arg);if(blk > 0 && blk <= DATA_SIZE){blocksize = blk;}else{printf("Error: blocksize should be > 0 && <= DATA_SIZE\n");}}}else if(strcmp(arg, "quit") == 0){break;}else{printf("Unknow command.\n");}}return 0;
}// Download a file from the server.
void do_get(char *remote_file, char *local_file){struct tftpx_packet snd_packet, rcv_packet;int next_block = 1;int recv_n;int total_bytes = 0;struct tftpx_packet ack;    struct sockaddr_in sender;int r_size = 0;int time_wait_data;ushort block = 1;// Send request.snd_packet.cmd = htons(CMD_RRQ);sprintf(snd_packet.filename, "%s%c%s%c%d%c", remote_file, 0, "octet", 0, blocksize, 0);sendto(sock, &snd_packet, sizeof(struct tftpx_packet), 0, (struct sockaddr*)&server, addr_len);FILE *fp = fopen(local_file, "w");if(fp == NULL){printf("Create file \"%s\" error.\n", local_file);return;}// Receive data.snd_packet.cmd = htons(CMD_ACK);do{for(time_wait_data = 0; time_wait_data < PKT_RCV_TIMEOUT * PKT_MAX_RXMT; time_wait_data += 10000){// Try receive(Nonblock receive).r_size = recvfrom(sock, &rcv_packet, sizeof(struct tftpx_packet), MSG_DONTWAIT,(struct sockaddr *)&sender,&addr_len);if(r_size > 0 && r_size < 4){printf("Bad packet: r_size=%d\n", r_size);}if(r_size >= 4 && rcv_packet.cmd == htons(CMD_DATA) && rcv_packet.block == htons(block)){printf("DATA: block=%d, data_size=%d\n", ntohs(rcv_packet.block), r_size - 4);// Send ACK.snd_packet.block = rcv_packet.block;sendto(sock, &snd_packet, sizeof(struct tftpx_packet), 0, (struct sockaddr*)&sender, addr_len);fwrite(rcv_packet.data, 1, r_size - 4, fp);break;}usleep(10000);}if(time_wait_data >= PKT_RCV_TIMEOUT * PKT_MAX_RXMT){printf("Wait for DATA #%d timeout.\n", block);goto do_get_error;}block ++;}while(r_size == blocksize + 4);//printf("\nReceived %d bytes.\n", total_bytes);do_get_error:fclose(fp);
}// Upload a file to the server.
void do_put(char *filename){struct sockaddr_in sender;struct tftpx_packet rcv_packet, snd_packet;int r_size = 0;int time_wait_ack;// Send request and wait for ACK#0.snd_packet.cmd = htons(CMD_WRQ);sprintf(snd_packet.filename, "%s%c%s%c%d%c", filename, 0, "octet", 0, blocksize, 0); sendto(sock, &snd_packet, sizeof(struct tftpx_packet), 0, (struct sockaddr*)&server, addr_len); for(time_wait_ack = 0; time_wait_ack < PKT_RCV_TIMEOUT; time_wait_ack += 20000){// Try receive(Nonblock receive).r_size = recvfrom(sock, &rcv_packet, sizeof(struct tftpx_packet), MSG_DONTWAIT,(struct sockaddr *)&sender,&addr_len);if(r_size > 0 && r_size < 4){printf("Bad packet: r_size=%d\n", r_size);}if(r_size >= 4 && rcv_packet.cmd == htons(CMD_ACK) && rcv_packet.block == htons(0)){break;}usleep(20000);}if(time_wait_ack >= PKT_RCV_TIMEOUT){printf("Could not receive from server.\n");return;}FILE *fp = fopen(filename, "r");if(fp == NULL){printf("File not exists!\n");return;}int s_size = 0;int rxmt;ushort block = 1;snd_packet.cmd = htons(CMD_DATA);// Send data.do{memset(snd_packet.data, 0, sizeof(snd_packet.data));snd_packet.block = htons(block);s_size = fread(snd_packet.data, 1, blocksize, fp);for(rxmt = 0; rxmt < PKT_MAX_RXMT; rxmt ++){sendto(sock, &snd_packet, s_size + 4, 0, (struct sockaddr*)&sender, addr_len);printf("Send %d\n", block);// Wait for ACK.for(time_wait_ack = 0; time_wait_ack < PKT_RCV_TIMEOUT; time_wait_ack += 20000){// Try receive(Nonblock receive).r_size = recvfrom(sock, &rcv_packet, sizeof(struct tftpx_packet), MSG_DONTWAIT,(struct sockaddr *)&sender,&addr_len);if(r_size > 0 && r_size < 4){printf("Bad packet: r_size=%d\n", r_size);}if(r_size >= 4 && rcv_packet.cmd == htons(CMD_ACK) && rcv_packet.block == htons(block)){break;}usleep(20000);}if(time_wait_ack < PKT_RCV_TIMEOUT){// Send success.break;}else{// Retransmission.continue;}}if(rxmt >= PKT_MAX_RXMT){printf("Could not receive from server.\n");return;}block ++;}while(s_size == blocksize);printf("\nSend file end.\n");do_put_error:fclose(fp);return;
}// Directory listing.
void do_list(int sock, char *dir){struct tftpx_packet packet;   int next_block = 1;int recv_n;struct tftpx_packet ack; struct sockaddr_in sender;ack.cmd = htons(CMD_ACK);int r_size = 0;int time_wait_data;ushort block = 1;// Send request.packet.cmd = htons(CMD_LIST);strcpy(packet.data, dir);sendto(sock, &packet, sizeof(struct tftpx_packet), 0, (struct sockaddr*)&server, addr_len);printf("type\tsize\tname\n");printf("-------------------------------------------------\n");// Receive data.do{for(time_wait_data = 0; time_wait_data < PKT_RCV_TIMEOUT * PKT_MAX_RXMT; time_wait_data += 20000){// Try receive(Nonblock receive).r_size = recvfrom(sock, &packet, sizeof(packet), MSG_DONTWAIT,(struct sockaddr *)&sender,&addr_len);if(r_size > 0 && r_size < 4){printf("Bad packet: r_size=%d\n", r_size);}if(r_size >= 4 && packet.cmd == htons(CMD_DATA) && packet.block == htons(block)){block ++;ack.block = packet.block;sendto(sock, &ack, sizeof(struct tftpx_packet), 0, (struct sockaddr*)&sender, addr_len);fwrite(packet.data, 1, r_size - 4, stdout);break;}usleep(20000);}if(time_wait_data >= PKT_RCV_TIMEOUT * PKT_MAX_RXMT){printf("Wait for DATA #%d timeout.\n", block);return;}}while(r_size == blocksize + 4);
}

client.h 包含文件

/*********************************************** Author: ideawu(www.ideawu.net)* Date: 2007-06* File: client.h*********************************************/#include "tftpx.h"#define LINE_BUF_SIZE 1024void do_list(int sock, char *dir);
void do_get(char *remote_file, char *local_file);
void do_put(char *filename);

tftpx.h 包含文件

/*********************************************** Author: ideawu(www.ideawu.net)* Date: 2007-04* File: tftpx.h*********************************************/#ifndef TFTPX_H
#define TFTPX_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <dirent.h>#define CMD_RRQ (short)1
#define CMD_WRQ (short)2
#define CMD_DATA (short)3
#define CMD_ACK (short)4
#define CMD_ERROR (short)5
#define CMD_LIST (short)6
#define CMD_HEAD (short)7// Without a '/' at the end.
char *conf_document_root;#define SERVER_PORT 10220
// Max request datagram size
#define MAX_REQUEST_SIZE 1024
// TFTPX_DATA_SIZE
#define DATA_SIZE 512
//
#define LIST_BUF_SIZE (DATA_SIZE * 8)// Max packet retransmission.
#define PKT_MAX_RXMT 3
// usecond
#define PKT_SND_TIMEOUT 12*1000*1000
#define PKT_RCV_TIMEOUT 3*1000*1000
// usecond
#define PKT_TIME_INTERVAL 5*1000struct tftpx_packet{ushort cmd;union{ushort code;ushort block;// For a RRQ and WRQ TFTP packetchar filename[2];};char data[DATA_SIZE];
};struct tftpx_request{int size;struct sockaddr_in client;struct tftpx_packet packet;
};#endif/*
Error CodesValue     Meaning0         Not defined, see error message (if any).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.
*/

服务端程序可以从上面链接下载。

这里只是介绍客户端代码,但包含的发送和接收机制都是一样的。

谢谢阅读。

tftp 源代码解析相关推荐

  1. Spring源代码解析(收藏)

    Spring源代码解析(收藏) Spring源代码解析(一):IOC容器:http://www.iteye.com/topic/86339  Spring源代码解析(二):IoC容器在Web容器中的启 ...

  2. Android源代码解析之(四)--gt;HandlerThread

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们解说了AsyncTast的基本使用以及实现原理,我们知道AsyncTask内部是通过线程池和Handler实现的.通过对线程池和handler的封装实现 ...

  3. Tomcat源代码解析系列

    学web也有一段时间了.为了从底层了解web应用在Tomcat中的执行,决定看一下Tomcat的源代码參见<How Tomcat works>    和大牛博客.对大体架构有了一定的了解, ...

  4. 【backtrader源码解析52】indicators部分代码解读(枯燥,仅供参考,源代码解析结束,后面会增加一个backtrader框架分析)

    指标类里面有很多不同的指标,如果对如何使用指标做策略感兴趣的话,可以考虑阅读下相关的源码和指标的用法,就仅仅指标的源代码而言,似乎没有什么可讲的.另外,关于backtrader源代码的注释,放到网站上 ...

  5. TFTP协议解析及C/C++代码实现

    TFTP 用于以非常简单的方式传输文件.与其他文件传输协议(如:FTP 或 HTTP)相比,TFTP 更简单,代码量也更小,因此更易于实现. 通常,TFTP 使用 UDP 作为其传输协议.众所周知的 ...

  6. overlayfs源代码解析

    overlayfs源代码解析 mount挂载 注册文件系统 挂载文件系统 读写目录 openat系统调用打开目录 getdents系统调用,搜索目录 创建/删除文件 创建文件 删除文件 读写文件 读/ ...

  7. 历经一个月,终于搞定了SVM(支持向量机)-附源代码解析

    历经一个月,终于搞定了SVM(支持向量机)-附源代码解析 前言 其实整体算下来,断断续续的也得有快两个月了(原谅博主比较笨).中间也有好几次放弃,不想写这篇总结了,但是之前立下的誓言,要将学习到的每一 ...

  8. Mozilla FireFox Gecko内核源代码解析(4.nsHTMLTokens)

    之前我们分析了nsHTMLTokenizer(详见其解析篇),其中我们了解到了,其中设计了如何配合 nsScanner对输入流循环地解析流程,如怎么进行回溯等流式操作.实际上其中并没有包含具体的字符比 ...

  9. AnnaKournikova病毒源代码解析

    AnnaKournikova病毒源代码解析 AnnaKournikova病毒源代码解析 本帖版权归原作者,其它网站转载须注明出处,传统媒体转载须事先与原作者和e龙西祠胡同[http://www.xic ...

最新文章

  1. 高性能服务器存储服务器,高性能存储服务器
  2. 用JAVAMAIL发送邮件的一个简单例子
  3. gradle ant_使用Gradle引导旧式Ant构建
  4. mysql.data.dll win10_【MySQL】Windows10下安装
  5. 在Ubuntu上安装Keras深度学习框架
  6. c++语言的设计与演化 pdf_天津大学在金属材料织构设计与调控领域取得进展
  7. C++之指针探究(十五):回调函数应用之qsort排序
  8. Lua基本函数库 【转】
  9. 使用CHM文档 采集随笔(续)
  10. GEF: 图形拖拽处理
  11. STM32库函数模板创建
  12. 植物大战僵尸修改植物攻击力
  13. 方维直播源码无BUG修复最新版!
  14. 从十七年蝉谈起——hashtable的表格大小为什么要选择质数
  15. 马上过年了,还在为没抢到回家的车票天天犯愁吗?这些好用的抢票神器赶紧用起来吧!...
  16. 大数据十年:Cloudera向左,Palantir向右
  17. 各类开发生产环境缩写
  18. 树莓派 自动关闭屏幕解决办法
  19. PHP 生成随机昵称或用户名
  20. 2023考研数学真题及答案解析!

热门文章

  1. 令夫妻关系和谐的五大秘诀
  2. 基于模糊PID的混合磁悬浮减振器的设计及控制研究
  3. 51单片机太阳能风能风光互补路灯控制器设计
  4. 深入浅出谈人脸识别技术
  5. 计算机课件模拟实验,计算机模拟仿真密立根油滴实验课件.pdf
  6. Vision Transformer在CV任务中的速度如何保证?
  7. matlab plot函数 坐标轴标注
  8. python高德 查询县_Python获取高德地图省市区县列表
  9. 【手把手带你Godot游戏开发】FlappyBird:9.1 天下没有不OVER的GAME(上)
  10. 使用vuex实时更新右上角通知信息的红点数量