当前位置:   article > 正文

嵌入式学习——3——UDP TFTP简易文件传输

嵌入式学习——3——UDP TFTP简易文件传输

tftp协议概述

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

特点:

        是应用层协议

        基于UDP协议实现

数据传输模式

        octet:二进制模式(常用)

        mail:已经不再支持

TFTP通信过程总结

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

TFTP协议分析

差错码:

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.

  1. #include "../header.h"
  2. #define S_IP "192.168.125.30"
  3. #define S_PORT 69
  4. typedef struct file_header {
  5. int size;
  6. char *file_report;
  7. } file_header_report;
  8. char buf[516] = "";
  9. char fileName[128] = {0};
  10. int sockfd = -1;
  11. int data_num = 0;
  12. void download_file(struct sockaddr_in *addr);
  13. void upload_file(struct sockaddr_in *addr);
  14. void dealerror(short errno);
  15. file_header_report get_report_RW(short type);
  16. file_header_report get_report_ACK(short type, short blknumber);
  17. int main(int argc, char const *argv[]) {
  18. // 服务器地址 ,初始地址用于链接请求,但是不是用于实际数据交互
  19. struct sockaddr_in addr;
  20. addr.sin_family = AF_INET;
  21. addr.sin_port = htons(S_PORT);
  22. addr.sin_addr.s_addr = inet_addr(S_IP);
  23. char choose = 0;
  24. while (1) {
  25. printf("**************************\n");
  26. printf("******** 1. 下载 *********\n");
  27. printf("******** 2. 上传 *********\n");
  28. printf("******** 3. 退出 *********\n");
  29. printf("**************************\n");
  30. printf("请输入>>> ");
  31. choose = getchar();
  32. while (getchar() != 10) {
  33. };
  34. switch (choose) {
  35. case '1':
  36. download_file(&addr);
  37. break;
  38. case '2':
  39. upload_file(&addr);
  40. break;
  41. case '3':
  42. exit(EXIT_SUCCESS);
  43. break;
  44. default:
  45. printf("输入错误!请重新输入\n");
  46. }
  47. }
  48. close(sockfd);
  49. return 0;
  50. }
  51. void upload_file(struct sockaddr_in *addr) {
  52. if (sockfd == -1) { // 判断一下,防止重复创建套接字
  53. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  54. if (sockfd < 0) {
  55. perror("socket");
  56. return;
  57. }
  58. }
  59. // 发送上传请求
  60. printf("请输入要上传的文件名:\n");
  61. file_header_report file_report = get_report_RW(2);
  62. if (access(fileName, F_OK) != 0) {
  63. printf("文件不存在\n");
  64. return;
  65. }
  66. data_num = 0;
  67. if (sendto(sockfd, file_report.file_report, file_report.size, 0,
  68. (struct sockaddr *)addr, sizeof(struct sockaddr)) == -1) {
  69. perror("1111sendto 数据包");
  70. return;
  71. }
  72. // recv_addr 用于 实际数据交互的地址
  73. struct sockaddr_in recv_addr;
  74. socklen_t recv_addr_len = sizeof(recv_addr);
  75. int read_size = recvfrom(sockfd, buf, 516, 0, (struct sockaddr *)&recv_addr,
  76. &recv_addr_len);
  77. short *first_no = (short *)buf;
  78. short *blk_num = (short *)(buf + 2);
  79. // 处理报错信息 == 5
  80. if (ntohs(*first_no) == 5) {
  81. dealerror(ntohs(*blk_num));
  82. return;
  83. }
  84. if (ntohs(*first_no) == 4) {
  85. int upload_fd = open(fileName, O_RDONLY);
  86. if (upload_fd < 0) {
  87. perror("open");
  88. return;
  89. }
  90. while (1) {
  91. // 发送数据包
  92. bzero(buf, sizeof(buf));
  93. // 拼接数据包
  94. short *p1 = (short *)buf;
  95. *p1 = htons(3);
  96. short *p2 = (short *)(buf + 2);
  97. *p2 = htons(data_num);
  98. int file_size = read(upload_fd, buf + 4, 512);
  99. if (file_size < 0) {
  100. perror("read");
  101. exit(1);
  102. }
  103. if (sendto(sockfd, buf, file_size + 4, 0,
  104. (struct sockaddr *)&recv_addr,
  105. sizeof(recv_addr)) == -1) {
  106. perror("sendto 数据包");
  107. exit(1);
  108. }
  109. bzero(buf, sizeof(buf));
  110. int read_size_n =
  111. recvfrom(sockfd, buf, 4, 0, (struct sockaddr *)&recv_addr,
  112. &recv_addr_len);
  113. short *first_noe_n = (short *)buf;
  114. short *blk_nume_n = (short *)(buf + 2);
  115. // 确认ACK包
  116. // 编号是否与本地的标号一致,一致的话就进行下一个编号的数据传送
  117. if (ntohs(*first_noe_n) == 4 && ntohs(*blk_nume_n) == data_num) {
  118. // 这种情况说明本次上传成功
  119. data_num++;
  120. } else {
  121. printf("上传失败\n");
  122. if (ntohs(*first_no) == 5) {
  123. dealerror(ntohs(*blk_num));
  124. return;
  125. }
  126. break;
  127. }
  128. // 如果最后一次读取,就结束循环
  129. if (file_size < 512) {
  130. printf("上传成功\n");
  131. break;
  132. }
  133. }
  134. } else {
  135. printf("上传失败\n");
  136. }
  137. }
  138. // 下载
  139. void download_file(struct sockaddr_in *addr) {
  140. if (sockfd == -1) { // 判断一下,防止重复创建套接字
  141. sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  142. if (sockfd < 0) {
  143. perror("socket");
  144. return;
  145. }
  146. }
  147. // 发送下载请求
  148. printf("请输入要下载的文件名:\n");
  149. file_header_report file_report = get_report_RW(1);
  150. if (sendto(sockfd, file_report.file_report, file_report.size, 0,
  151. (struct sockaddr *)addr, sizeof(struct sockaddr)) == -1) {
  152. perror("sendto 数据包");
  153. return;
  154. }
  155. struct sockaddr_in recv_addr;
  156. socklen_t recv_addr_len = sizeof(recv_addr);
  157. while (1) {
  158. // 接收数据包
  159. // 数据包返回的服务器端地址需要存在recv_addr,因为服务器处理数据的端口并非原来端口,
  160. // 该地址recv_addr 用于后续的ack包 发送给服务器用于确认
  161. bzero(buf, sizeof(buf));
  162. int read_size = recvfrom(sockfd, buf, 516, 0,
  163. (struct sockaddr *)&recv_addr, &recv_addr_len);
  164. int download_fd = open(fileName, O_RDWR | O_CREAT | O_APPEND, 0666);
  165. if (download_fd < 0) {
  166. perror("open");
  167. return;
  168. }
  169. short *first_no = (short *)buf;
  170. short *blk_num = (short *)(buf + 2);
  171. // 处理报错信息 == 5
  172. if (ntohs(*first_no) == 5) {
  173. dealerror(ntohs(*blk_num));
  174. break;
  175. }
  176. // 这是数据包 == 3
  177. if (ntohs(*first_no) == 3) {
  178. if (read_size > 4) {
  179. write(download_fd, buf + 4, read_size - 4);
  180. }
  181. // 发送ACK
  182. *first_no = htons(4);
  183. char newbuf[4] = {0};
  184. short *p1 = (short *)newbuf;
  185. *p1 = htons(4);
  186. short *p2 = (short *)(newbuf + 2);
  187. *p2 = *blk_num;
  188. if (sendto(sockfd, newbuf, 4, 0, (struct sockaddr *)&recv_addr,
  189. sizeof(recv_addr)) == -1) {
  190. perror("sendtoACK\n");
  191. }
  192. printf("发送ACK成功\n");
  193. if (read_size < 516) {
  194. printf("下载完成\n");
  195. break;
  196. }
  197. }
  198. close(download_fd);
  199. }
  200. }
  201. // 处理报错信息
  202. void dealerror(short errno) {
  203. int erno = 0;
  204. switch (errno) {
  205. case 1:
  206. printf("File not found");
  207. break;
  208. case 2:
  209. printf("Access violation");
  210. break;
  211. case 3:
  212. printf("Disk full or allocation exceeded");
  213. break;
  214. case 4:
  215. printf("illegal TFTP operation");
  216. break;
  217. case 5:
  218. printf("Unknown transfer lD");
  219. break;
  220. case 6:
  221. printf("File already exists");
  222. break;
  223. case 7:
  224. printf("No such user");
  225. break;
  226. case 8:
  227. printf("Unsupported option(s) requested");
  228. break;
  229. default:
  230. break;
  231. }
  232. return;
  233. }
  234. /**
  235. * @description: 组装请求体数据包
  236. * @param {short} type 1--- 下载 2---上传
  237. * @return {*}
  238. */
  239. file_header_report get_report_RW(short type) {
  240. bzero(buf, sizeof(buf));
  241. fgets(fileName, sizeof(fileName), stdin);
  242. if (strlen(fileName) > 1) {
  243. fileName[strlen(fileName) - 1] = '\0';
  244. }
  245. short *p1 = (short *)buf;
  246. *p1 = htons(type);
  247. char *p2 = buf + 2;
  248. strcpy(p2, fileName);
  249. char *p4 = p2 + strlen(p2) + 1;
  250. strcpy(p4, "octet");
  251. int size = 2 + strlen(p2) + strlen(p4) + 2;
  252. file_header_report fh = {size, buf};
  253. return fh;
  254. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/639556
推荐阅读
相关标签
  

闽ICP备14008679号