• 嵌入式学习——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. }

  • 相关阅读:
    @Valid与@Validated区别和详细使用及参数注解校验大全
    OS>>信号的产生,信号的保存,信号的捕捉
    怎么查找html元素
    stu01-IDEA怎么创建一个HTML项目
    【三维目标检测】3DSSD(二)
    第11章 初识SqlSugarCore之NPOI Excel导入
    滚雪球学Java(43):探究 Java 中的 Class 类:透视类的本质和实现原理
    vue3.2 之 i18n的使用
    [附源码]JAVA毕业设计考研驿站网站(系统+LW)
    【Java|golang】658. 找到 K 个最接近的元素
  • 原文地址:https://blog.csdn.net/qq_37193153/article/details/139100903