当前位置:   article > 正文

基于rt-thread的udp客户端_rtthread udp

rtthread udp
基于rt-thread的udp客户端


本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.

 

环境:

开发环境:MDK5.23

rt-thread版本:2.1.0

lwip版本:1.4.1

单片机型号:stm32f407

phy芯片型号:


说明:
本程序是udp客户端模块。绑定固定端口进行收发。udp接收是一个线程,接收到数据利用邮箱机制推送到其他处理模块。udp发送提供了多种发送接口。

源码:
udp_socket.h
  1. /**
  2. * Copyright (c), 2015-2025
  3. * @file udp_socket.h
  4. * @brief udp端口头文件
  5. * @author jdh
  6. * @verbatim
  7. * Change Logs:
  8. * Date Author Notes
  9. * 2017-12-22 jdh 新建
  10. * @endverbatim
  11. */
  12. #ifndef _UDP_SOCKET_H_
  13. #define _UDP_SOCKET_H_
  14. #include "drivers.h"
  15. /**
  16. * @brief 最大注册邮箱数
  17. */
  18. #define MAX_NUM_UDP_SOCKET_MAILBOX 10
  19. /**
  20. * @brief 接收数据结构.邮箱会推送此结构指针
  21. */
  22. struct UdpSocketRx
  23. {
  24. T_Buffer_Large buffer;
  25. struct sockaddr_in sockaddr;
  26. };
  27. /**
  28. * @brief 模块载入
  29. */
  30. void udp_socket_load(void);
  31. /**
  32. * @brief 更新服务器信息
  33. */
  34. void udp_socket_update_server_info(void);
  35. /**
  36. * @brief socket是否工作
  37. * @return true: 工作.false: 不工作
  38. */
  39. bool udp_socket_is_work(void);
  40. /**
  41. * @brief 发送数据
  42. * @param data:发送数据存放地址
  43. * @param size:发送数据字节数
  44. * @param dst_ip: 目的ip
  45. * @param dst_port: 目的端口
  46. */
  47. void udp_socket_tx(uint8_t *data, uint16_t size, char *dst_ip, uint16_t dst_port);
  48. /**
  49. * @brief 发送数据
  50. * @param data:发送数据存放地址
  51. * @param size:发送数据字节数
  52. * @param sockaddr: 目的地址
  53. */
  54. void udp_socket_tx_sockaddr(uint8_t *data, uint16_t size, struct sockaddr_in sockaddr);
  55. /**
  56. * @brief 发送数据给服务器
  57. * @param data:发送数据存放地址
  58. * @param size:发送数据字节数
  59. */
  60. void udp_socket_tx_server(uint8_t *data, uint16_t size);
  61. /**
  62. * @brief 发送数据给配置服务器
  63. * @note 配置服务器无效则发送给服务器
  64. * @param data:发送数据存放地址
  65. * @param size:发送数据字节数
  66. */
  67. void udp_socket_tx_config_server(uint8_t *data, uint16_t size);
  68. /**
  69. * @brief 注册邮箱
  70. * @note 接收数据后会推送到此邮箱
  71. * @param mailbox: 邮箱地址
  72. */
  73. void udp_socket_register_mailbox(rt_mailbox_t mailbox);
  74. /**
  75. * @brief 增加传输层头后发送数据
  76. * @param dst_device: 目标设备类型
  77. * @param data: 发送数据存放地址
  78. * @param size: 发送数据字节数
  79. * @param sockaddr: 目的地址
  80. */
  81. void udp_socket_tx_sockaddr_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size, struct sockaddr_in sockaddr);
  82. /**
  83. * @brief 发送确认帧
  84. * @param dst_device: 目标设备类型
  85. * @param cmd: 确认帧命令字
  86. * @param ack_cmd: 需要确认的命令
  87. * @param sockaddr: 目的地址
  88. */
  89. void udp_socket_tx_sockaddr_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd, struct sockaddr_in sockaddr);
  90. /**
  91. * @brief 增加传输层头后发送数据
  92. * @param dst_device: 目标设备类型
  93. * @param data: 发送数据存放地址
  94. * @param size: 发送数据字节数
  95. * @param sockaddr: 目的地址
  96. */
  97. void udp_socket_tx_server_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size);
  98. /**
  99. * @brief 发送确认帧
  100. * @param dst_device: 目标设备类型
  101. * @param cmd: 确认帧命令字
  102. * @param ack_cmd: 需要确认的命令
  103. * @param sockaddr: 目的地址
  104. */
  105. void udp_socket_tx_server_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd);
  106. #endif
udp_socket.c
  1. /**
  2. * Copyright (c), 2015-2025
  3. * @file udp_socket.c
  4. * @brief udp端口主文件
  5. * @author jdh
  6. * @verbatim
  7. * Change Logs:
  8. * Date Author Notes
  9. * 2017-12-22 jdh 新建
  10. * 2017-12-26 jdh 增加网络和lwip功能
  11. * @endverbatim
  12. */
  13. #include "framework.h"
  14. #include "stm32f4xx_eth.h"
  15. #include <netif/ethernetif.h>
  16. #define TAG "LOG_UDP"
  17. /**
  18. * @brief 配置帧超时时间.单位:ms.超过这个时间,射频模块的回复就不会再发向配置服务器
  19. */
  20. #define CONFIG_TIMEOUT 500
  21. /**
  22. * @brief 启动稳定期.单位:ms
  23. */
  24. #define STARTUP_WAIT_TIME 1000
  25. /**
  26. * @brief 日志项编号
  27. */
  28. static uint8_t _log_item = 0;
  29. static int _socket;
  30. static bool _is_net_work = false;
  31. static struct sockaddr_in _server_addr;
  32. static struct UdpSocketRx _udp_socket_rx;
  33. /**
  34. * @brief 配置服务器的地址
  35. */
  36. static struct sockaddr_in _config_server_addr;
  37. static T_Time _last_config_frame_time;
  38. /**
  39. * @brief 邮箱数组
  40. */
  41. static struct rt_mailbox *_mailbox_array[MAX_NUM_UDP_SOCKET_MAILBOX];
  42. static uint8_t _len_mailbox_array = 0;
  43. /**
  44. * @brief 发送数据
  45. */
  46. static T_Buffer_Large Buffer_Tx;
  47. static void thread_init(void* parameter);
  48. static void init_lwip(void);
  49. static void set_ip(void);
  50. static void bind_socket(void);
  51. static void socket_rx(void);
  52. static bool is_frame_valid(void);
  53. static inline void send_mailbox(void);
  54. /**
  55. * @brief 模块载入
  56. */
  57. void udp_socket_load(void)
  58. {
  59. _log_item = log_register(TAG);
  60. #ifdef RT_USING_LWIP
  61. rt_thread_t tid_init = rt_thread_create("init_net",
  62. thread_init, (void*)0,
  63. THREAD_STACK_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_SLICE_NORMAL);
  64. rt_thread_startup(tid_init);
  65. #endif
  66. }
  67. static void thread_init(void* parameter)
  68. {
  69. init_lwip();
  70. udp_socket_update_server_info();
  71. bind_socket();
  72. socket_rx();
  73. }
  74. static void init_lwip(void)
  75. {
  76. /* LwIP Initialization */
  77. {
  78. extern void lwip_sys_init(void);
  79. /* register ethernetif device */
  80. eth_system_device_init();
  81. // rt_hw_stm32_eth_init();
  82. rt_hw_stm32_eth_init_my();
  83. /* init lwip system */
  84. lwip_sys_init();
  85. rt_kprintf("TCP/IP initialized!\n");
  86. }
  87. set_ip();
  88. }
  89. static void set_ip(void)
  90. {
  91. set_if("e0", para_manage_read_ip(), para_manage_read_gateway(), para_manage_read_mask());
  92. set_dns(para_manage_read_dns());
  93. }
  94. /**
  95. * @brief 更新服务器信息
  96. */
  97. void udp_socket_update_server_info(void)
  98. {
  99. _server_addr.sin_family = AF_INET;
  100. _server_addr.sin_port = htons(para_manage_read_server_port());
  101. struct hostent *host;
  102. host = (struct hostent *)gethostbyname(para_manage_read_server_ip());
  103. _server_addr.sin_addr = *((struct in_addr *)host->h_addr);
  104. rt_memset(&(_server_addr.sin_zero), 0, sizeof(_server_addr.sin_zero));
  105. }
  106. static void bind_socket(void)
  107. {
  108. struct sockaddr_in local_addr;
  109. // 创建socket
  110. if ((_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
  111. {
  112. log_w(_log_item, "socket error\n");
  113. // todo
  114. return;
  115. }
  116. // 初始化本地地址
  117. local_addr.sin_family = AF_INET;
  118. local_addr.sin_port = htons(para_manage_read_port());
  119. local_addr.sin_addr.s_addr = INADDR_ANY;
  120. rt_memset(&(local_addr.sin_zero), 0, sizeof(local_addr.sin_zero));
  121. // 绑定端口
  122. if (bind(_socket, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)) == -1)
  123. {
  124. log_w(_log_item, "Bind error\n");
  125. // todo
  126. return;
  127. }
  128. thread_delay(STARTUP_WAIT_TIME);
  129. _is_net_work = true;
  130. }
  131. static void socket_rx(void)
  132. {
  133. rt_uint32_t addr_len;
  134. addr_len = sizeof(struct sockaddr);
  135. while (1)
  136. {
  137. _udp_socket_rx.buffer.len = recvfrom(_socket,
  138. _udp_socket_rx.buffer.buf,
  139. LEN_BUFFER_LARGE - 1,
  140. 0, (struct sockaddr *)&_udp_socket_rx.sockaddr, &addr_len);
  141. if (is_frame_valid())
  142. {
  143. log_i(_log_item, "udp rx ip:%s port:%02d\n", inet_ntoa(_udp_socket_rx.sockaddr.sin_addr.s_addr),
  144. ntohs(_udp_socket_rx.sockaddr.sin_port));
  145. if (_udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_STATION)
  146. {
  147. // 发送给本机
  148. send_mailbox();
  149. }
  150. else
  151. {
  152. // 转发
  153. uart_tx(_udp_socket_rx.buffer.buf[PTH_ADDITIONAL_POS], _udp_socket_rx.buffer.buf, _udp_socket_rx.buffer.len);
  154. // 保存配置服务器信息
  155. if (_udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_TIME_MODULE ||
  156. _udp_socket_rx.buffer.buf[PTH_DST_DEVICE_POS] == DEVICE_RADIO_MODULE)
  157. {
  158. _config_server_addr = _udp_socket_rx.sockaddr;
  159. _last_config_frame_time = get_local_time();
  160. }
  161. }
  162. led_blink(LED_RX_NET);
  163. log_add_num_rx_udp_frame();
  164. }
  165. }
  166. }
  167. static bool is_frame_valid(void)
  168. {
  169. if (_udp_socket_rx.buffer.len < PTH_LEN_FRAME_HEAD)
  170. {
  171. return false;
  172. }
  173. uint16_t frame_head = (_udp_socket_rx.buffer.buf[PTH_LEN_FRAME_HEAD_POS] << 8) +
  174. _udp_socket_rx.buffer.buf[PTH_LEN_FRAME_HEAD_POS + 1];
  175. if (frame_head != PTH_HEAD)
  176. {
  177. return false;
  178. }
  179. uint16_t body_len = (_udp_socket_rx.buffer.buf[PTH_BODY_LEN_POS] << 8) + _udp_socket_rx.buffer.buf[PTH_BODY_LEN_POS + 1];
  180. if (_udp_socket_rx.buffer.len != body_len + PTH_LEN_FRAME_HEAD)
  181. {
  182. return false;
  183. }
  184. if (_udp_socket_rx.buffer.buf[PTH_ADDITIONAL_POS] > MODULE_NUM)
  185. {
  186. return false;
  187. }
  188. uint16_t crc_get = (_udp_socket_rx.buffer.buf[PTH_BODY_CRC_POS] << 8) + _udp_socket_rx.buffer.buf[PTH_BODY_CRC_POS + 1];
  189. uint16_t crc_calc = crc_code(_udp_socket_rx.buffer.buf + PTH_LEN_FRAME_HEAD, body_len);
  190. if (crc_get != crc_calc)
  191. {
  192. return false;
  193. }
  194. return true;
  195. }
  196. static inline void send_mailbox(void)
  197. {
  198. for (uint8_t i = 0; i < _len_mailbox_array; i++)
  199. {
  200. rt_mb_send(_mailbox_array[i], (rt_uint32_t)&_udp_socket_rx);
  201. }
  202. }
  203. /**
  204. * @brief socket是否工作
  205. * @return true: 工作.false: 不工作
  206. */
  207. bool udp_socket_is_work(void)
  208. {
  209. return _is_net_work;
  210. }
  211. /**
  212. * @brief 发送数据
  213. * @param data:发送数据存放地址
  214. * @param size:发送数据字节数
  215. * @param dst_ip: 目的ip
  216. * @param dst_port: 目的端口
  217. */
  218. void udp_socket_tx(uint8_t *data, uint16_t size, char *dst_ip, uint16_t dst_port)
  219. {
  220. if (!_is_net_work)
  221. {
  222. return;
  223. }
  224. struct sockaddr_in remote_addr;
  225. remote_addr.sin_family = AF_INET;
  226. remote_addr.sin_port = htons(dst_port);
  227. struct hostent *host;
  228. host = (struct hostent *)gethostbyname(dst_ip);
  229. remote_addr.sin_addr = *((struct in_addr *)host->h_addr);
  230. rt_memset(&(remote_addr.sin_zero), 0, sizeof(remote_addr.sin_zero));
  231. sendto(_socket, data, size, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr));
  232. led_blink(LED_TX_NET);
  233. log_add_num_tx_udp_frame();
  234. }
  235. /**
  236. * @brief 发送数据
  237. * @param data:发送数据存放地址
  238. * @param size:发送数据字节数
  239. * @param sockaddr: 目的地址
  240. */
  241. void udp_socket_tx_sockaddr(uint8_t *data, uint16_t size, struct sockaddr_in sockaddr)
  242. {
  243. if (!_is_net_work)
  244. {
  245. return;
  246. }
  247. sendto(_socket, data, size, 0, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr));
  248. led_blink(LED_TX_NET);
  249. log_add_num_tx_udp_frame();
  250. }
  251. /**
  252. * @brief 发送数据给服务器
  253. * @param data:发送数据存放地址
  254. * @param size:发送数据字节数
  255. */
  256. void udp_socket_tx_server(uint8_t *data, uint16_t size)
  257. {
  258. if (!_is_net_work)
  259. {
  260. return;
  261. }
  262. sendto(_socket, data, size, 0, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr));
  263. led_blink(LED_TX_NET);
  264. log_add_num_tx_udp_frame();
  265. }
  266. /**
  267. * @brief 发送数据给配置服务器
  268. * @note 配置服务器无效则发送给服务器
  269. * @param data:发送数据存放地址
  270. * @param size:发送数据字节数
  271. */
  272. void udp_socket_tx_config_server(uint8_t *data, uint16_t size)
  273. {
  274. if (!_is_net_work)
  275. {
  276. return;
  277. }
  278. bool is_valid = false;
  279. T_Time time = get_local_time();
  280. if (time.s - _last_config_frame_time.s < 2)
  281. {
  282. if (sub_time(get_local_time(), _last_config_frame_time) < CONFIG_TIMEOUT * 1000)
  283. {
  284. is_valid = true;
  285. }
  286. }
  287. if (is_valid)
  288. {
  289. sendto(_socket, data, size, 0, (struct sockaddr *)&_config_server_addr, sizeof(struct sockaddr));
  290. }
  291. else
  292. {
  293. sendto(_socket, data, size, 0, (struct sockaddr *)&_server_addr, sizeof(struct sockaddr));
  294. }
  295. led_blink(LED_TX_NET);
  296. log_add_num_tx_udp_frame();
  297. }
  298. /**
  299. * @brief 注册邮箱
  300. * @note 接收数据后会推送到此邮箱
  301. * @param mailbox: 邮箱地址
  302. */
  303. void udp_socket_register_mailbox(rt_mailbox_t mailbox)
  304. {
  305. _mailbox_array[_len_mailbox_array++] = mailbox;
  306. }
  307. /**
  308. * @brief 增加传输层头后发送数据
  309. * @param dst_device: 目标设备类型
  310. * @param data: 发送数据存放地址
  311. * @param size: 发送数据字节数
  312. * @param sockaddr: 目的地址
  313. */
  314. void udp_socket_tx_sockaddr_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size, struct sockaddr_in sockaddr)
  315. {
  316. if (size > PTH_MAX_BODY_LEN)
  317. {
  318. return;
  319. }
  320. // 帧头
  321. Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS] = PTH_HEAD >> 8;
  322. Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS + 1] = PTH_HEAD & 0xff;
  323. // 源设备类型
  324. Buffer_Tx.buf[PTH_SRC_DEVICE_POS] = DEVICE_MY;
  325. // 目的设备类型
  326. Buffer_Tx.buf[PTH_DST_DEVICE_POS] = dst_device;
  327. // 附加信息
  328. Buffer_Tx.buf[PTH_ADDITIONAL_POS] = 0;
  329. // 正文长度
  330. Buffer_Tx.buf[PTH_BODY_LEN_POS] = size >> 8;
  331. Buffer_Tx.buf[PTH_BODY_LEN_POS + 1] = size;
  332. // 正文CRC
  333. uint16_t crc_calc = crc_code(data, size);
  334. Buffer_Tx.buf[PTH_BODY_CRC_POS] = crc_calc >> 8;
  335. Buffer_Tx.buf[PTH_BODY_CRC_POS + 1] = crc_calc;
  336. // 正文
  337. memcpy(Buffer_Tx.buf + PTH_LEN_FRAME_HEAD, data, size);
  338. Buffer_Tx.len = size + PTH_LEN_FRAME_HEAD;
  339. udp_socket_tx_sockaddr(Buffer_Tx.buf, Buffer_Tx.len, sockaddr);
  340. }
  341. /**
  342. * @brief 发送确认帧
  343. * @param dst_device: 目标设备类型
  344. * @param cmd: 确认帧命令字
  345. * @param ack_cmd: 需要确认的命令
  346. * @param sockaddr: 目的地址
  347. */
  348. void udp_socket_tx_sockaddr_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd, struct sockaddr_in sockaddr)
  349. {
  350. T_Buffer buffer;
  351. buffer.len = 0;
  352. buffer.buf[buffer.len++] = cmd;
  353. buffer.buf[buffer.len++] = ack_cmd;
  354. udp_socket_tx_sockaddr_add_trans_head(dst_device, buffer.buf, buffer.len, sockaddr);
  355. }
  356. /**
  357. * @brief 增加传输层头后发送数据
  358. * @param dst_device: 目标设备类型
  359. * @param data: 发送数据存放地址
  360. * @param size: 发送数据字节数
  361. * @param sockaddr: 目的地址
  362. */
  363. void udp_socket_tx_server_add_trans_head(uint8_t dst_device, uint8_t *data, uint16_t size)
  364. {
  365. if (size > PTH_MAX_BODY_LEN)
  366. {
  367. return;
  368. }
  369. // 帧头
  370. Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS] = PTH_HEAD >> 8;
  371. Buffer_Tx.buf[PTH_LEN_FRAME_HEAD_POS + 1] = PTH_HEAD & 0xff;
  372. // 源设备类型
  373. Buffer_Tx.buf[PTH_SRC_DEVICE_POS] = DEVICE_MY;
  374. // 目的设备类型
  375. Buffer_Tx.buf[PTH_DST_DEVICE_POS] = dst_device;
  376. // 附加信息
  377. Buffer_Tx.buf[PTH_ADDITIONAL_POS] = 0;
  378. // 正文长度
  379. Buffer_Tx.buf[PTH_BODY_LEN_POS] = size >> 8;
  380. Buffer_Tx.buf[PTH_BODY_LEN_POS + 1] = size;
  381. // 正文CRC
  382. uint16_t crc_calc = crc_code(data, size);
  383. Buffer_Tx.buf[PTH_BODY_CRC_POS] = crc_calc >> 8;
  384. Buffer_Tx.buf[PTH_BODY_CRC_POS + 1] = crc_calc;
  385. // 正文
  386. memcpy(Buffer_Tx.buf + PTH_LEN_FRAME_HEAD, data, size);
  387. Buffer_Tx.len = size + PTH_LEN_FRAME_HEAD;
  388. udp_socket_tx_server(Buffer_Tx.buf, Buffer_Tx.len);
  389. }
  390. /**
  391. * @brief 发送确认帧
  392. * @param dst_device: 目标设备类型
  393. * @param cmd: 确认帧命令字
  394. * @param ack_cmd: 需要确认的命令
  395. * @param sockaddr: 目的地址
  396. */
  397. void udp_socket_tx_server_ack_frame(uint8_t dst_device, uint8_t cmd, uint8_t ack_cmd)
  398. {
  399. T_Buffer buffer;
  400. buffer.len = 0;
  401. buffer.buf[buffer.len++] = cmd;
  402. buffer.buf[buffer.len++] = ack_cmd;
  403. udp_socket_tx_server(Buffer_Tx.buf, Buffer_Tx.len);
  404. }
接收的应用模块示例:
  1. /**
  2. * Copyright (c), 2015-2025
  3. * @file remote_reset.c
  4. * @brief 远程复位功能模块主文件
  5. * @author jdh
  6. * @verbatim
  7. * Change Logs:
  8. * Date Author Notes
  9. * 2018-01-08 jdh 新建
  10. * @endverbatim
  11. */
  12. #include "remote_reset.h"
  13. #include "protocol.h"
  14. #define TAG "LOG:REMOTE_RESET"
  15. /**
  16. * @brief 日志项编号
  17. */
  18. static uint8_t _log_item = 0;
  19. static void thread_udp_rx(void* parameter);
  20. /**
  21. * @brief 模块载入
  22. */
  23. void remote_reset_load(void)
  24. {
  25. _log_item = log_register(TAG);
  26. rt_thread_t tid_udp_rx = rt_thread_create("rr_udp_rx",
  27. thread_udp_rx, (void*)0,
  28. THREAD_STACK_BIG, THREAD_PRIORITY_NORMAL, THREAD_SLICE_NORMAL);
  29. rt_thread_startup(tid_udp_rx);
  30. }
  31. static void thread_udp_rx(void* parameter)
  32. {
  33. rt_mailbox_t mb = rt_mb_create("mb_udp_rx", 32, RT_IPC_FLAG_FIFO);
  34. udp_socket_register_mailbox(mb);
  35. struct UdpSocketRx *udp_socket_rx;
  36. while (1)
  37. {
  38. if (rt_mb_recv(mb, (rt_uint32_t *)&udp_socket_rx, RT_WAITING_FOREVER) == RT_EOK)
  39. {
  40. if (udp_socket_rx->buffer.buf[PTH_SRC_DEVICE_POS] == DEVICE_SERVER)
  41. {
  42. uint8_t cmd = udp_socket_rx->buffer.buf[PTH_LEN_FRAME_HEAD];
  43. switch (cmd)
  44. {
  45. case PSRAS_RESET:
  46. {
  47. log_w(_log_item, "udp rx remote reset cmd\n");
  48. // 应答
  49. udp_socket_tx_sockaddr_ack_frame(DEVICE_SERVER, PSRAS_ACK, PSRAS_RESET, udp_socket_rx->sockaddr);
  50. // 复位生效
  51. reset_manage_delay_reset();
  52. break;
  53. }
  54. }
  55. }
  56. }
  57. }
  58. }

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

闽ICP备14008679号