0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深入了解IP数据报发送的过程

汽车电子技术 来源:物联网IoT开发 作者:杰杰mcu 2023-02-14 10:24 次阅读

IP协议的功能

回顾一下前面写的关于IP协议的文章:

IP协议基础扫盲班

IP地址相关知识深入了解~

IP数据报分析

IP数据报结构、IP分片的原理与处理

IP数据报的收发

回顾一下前面的文章所提及的知识点,总结一下IP协议的功能,得到以下结论:

  1. 编址(目标端的IP地址),数据传输的过程当中就必须表明要发送目标端的IP地址
  2. 寻址和路由(根据对方的IP地址,寻找最佳路径传输信息);
  3. 数据报的分片和重组。
  4. 传递服务是不可靠的(IP协议只是尽自己最大努力去传输数据包),它也是无连接的协议

IP数据报发送

IP协议是网络层的主要协议,在上层传输协议(如TCP/UDP)需要发送数据时,会将数据封装起来,然后传递到IP层,IP协议首先会根据上层协议的目标IP地址选择一个合适的网卡进行发送数据( 路由),然后IP协议将再次封装数据形成IP数据报,主要的操作就是填写IP数据报首部对应的各个字段:目标IP地址、源IP地址、协议类型、生存时间等,最后在IP层通过回调函数netif->output(即etharp_output()函数)将IP数据报投递给ARP,再调用网卡底层发送函数进行发送,这样子自上而下的数据就发送出去,IP协议以目标IP地址作为目标主机的身份地址。

  1. /**

  2. * ip_output_if的简化版接口。它找到发送数据包的netif网络接口并调用ip_output_if来完成实际工作。

  3. *

  4. * @param p 要发送的数据包(p->payload(有效负载)指向数据,如果dest == LWIP_IP_HDRINCL,则p已包含IP头和p->有效负载指向该IP头)

  5. * @param src 要发送的源IP地址(如果src == IP4_ADDR_ANY,则用发送的netif绑定的IP地址用作源地址)

  6. * @param dest 目的IP地址

  7. * @param ttl 要在IP标头中设置的TTL值(生存时间)

  8. * @param tos 用于在IP标头中设置的TOS值

  9. * @param proto 将在IP头中设置对应的上层协议

  10. * @return ERR_OK 如果数据包发送正常就返回ok,

  11. * 如果p没有足够的空间用于IP /LINK标头,则为ERR_BUF

  12. * 其他则返回netif->output返回的错误

  13. * @return ERR_RTE如果没有找到路线

  14. * 请参阅ip_output_if()以获取更多返回值

  15. */

  16. err_t

  17. ip4_output(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,

  18. u8_t ttl,u8_t tos,u8_t proto)

  19. {

  20. struct netif *netif;


  21. LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);


  22. //根据目标IP地址找到对应的网卡发送数据

  23. if((netif = ip4_route_src(src, dest))== NULL){

  24. LWIP_DEBUGF(IP_DEBUG,("ip4_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\\n",

  25. ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));

  26. IP_STATS_INC(ip.rterr);

  27. return ERR_RTE;

  28. }


  29. return ip4_output_if(p, src, dest, ttl, tos, proto, netif);

  30. }

路由过程的实现

路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动,发送端必然需要找到一个网卡将数据报发送出去,而实现这个过程的函数就是 ip4_route_src()

其实lwip对 ip4_route_src()函数进行了重新定义,实际上是调用了 ip4_route()函数。这个函数的原理就是根据指定的IP地址找到合适的网卡 netif,然后返回,前面的文章也提到过,lwip的网卡是通过 netif_list列表管理的,那么找网卡的操作也必然是遍历网卡列表 netif_list,判断网卡是否已经挂载并且IP地址是否有效,如果连网卡都找不到,那就不用发送数据了,返回null。

  1. #define ip4_route_src(src, dest) ip4_route(dest)

  1. /**

  2. *为给定的IP地址查找适当的网络接口。

  3. *它搜索网络接口列表。找到匹配项

  4. *

  5. *@param dest 要查找路由的目标IP地址

  6. *@return 发送到达目的地的网卡 netif

  7. */

  8. struct netif *

  9. ip4_route(constip4_addr_t*dest)

  10. {

  11. #if!LWIP_SINGLE_NETIF

  12. struct netif *netif;


  13. LWIP_ASSERT_CORE_LOCKED();


  14. #if LWIP_MULTICAST_TX_OPTIONS

  15. /*默认使用管理选择的接口进行多播*/

  16. if(ip4_addr_ismulticast(dest)&& ip4_default_multicast_netif){

  17. return ip4_default_multicast_netif;

  18. }

  19. #endif /* LWIP_MULTICAST_TX_OPTIONS */


  20. /* bug #54569: in case LWIP_SINGLE_NETIF=1 and LWIP_DEBUGF() disabled, the following loop is optimized away */

  21. LWIP_UNUSED_ARG(dest);


  22. /*遍历网卡列表netif_list */

  23. NETIF_FOREACH(netif){

  24. /* 如果网卡已经挂载并且IP地址是有效的 */

  25. if(netif_is_up(netif)&& netif_is_link_up(netif)&&!ip4_addr_isany_val(*netif_ip4_addr(netif))){

  26. /* 网络掩码匹配? */

  27. if(ip4_addr_netcmp(dest, netif_ip4_addr(netif), netif_ip4_netmask(netif))){

  28. /* 返回找到的网卡netif */

  29. return netif;

  30. }

  31. /* 网关在非广播接口上匹配?(即在点对点接口中对等) */

  32. if(((netif->flags & NETIF_FLAG_BROADCAST)==0)&& ip4_addr_cmp(dest, netif_ip4_gw(netif))){

  33. /* 返回找到的网卡netif */

  34. return netif;

  35. }

  36. }

  37. }


  38. #if LWIP_NETIF_LOOPBACK &&!LWIP_HAVE_LOOPIF /**如果打开环回地址的宏定义 */

  39. /* loopif is disabled, looopback traffic is passed through any netif */

  40. if(ip4_addr_isloopback(dest)){

  41. /*不检查环回流量的链接*/

  42. if(netif_default != NULL && netif_is_up(netif_default)){

  43. return netif_default;

  44. }

  45. /*默认netif没有启动,只需使用任何netif进行环回流量*/

  46. NETIF_FOREACH(netif){

  47. if(netif_is_up(netif)){

  48. return netif;

  49. }

  50. }

  51. return NULL;

  52. }

  53. #endif /* LWIP_NETIF_LOOPBACK && !LWIP_HAVE_LOOPIF */


  54. #ifdef LWIP_HOOK_IP4_ROUTE_SRC

  55. netif = LWIP_HOOK_IP4_ROUTE_SRC(NULL, dest);

  56. if(netif != NULL){

  57. return netif;

  58. }

  59. #elif defined(LWIP_HOOK_IP4_ROUTE)

  60. netif = LWIP_HOOK_IP4_ROUTE(dest);

  61. if(netif != NULL){

  62. return netif;

  63. }

  64. #endif

  65. #endif /* !LWIP_SINGLE_NETIF */


  66. if((netif_default == NULL)||!netif_is_up(netif_default)||!netif_is_link_up(netif_default)||

  67. ip4_addr_isany_val(*netif_ip4_addr(netif_default))|| ip4_addr_isloopback(dest)){

  68. /*找不到匹配的netif,默认的netif不可用。建议使用LWIP_HOOK_IP4_ROUTE()*/

  69. LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\\n",

  70. ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));

  71. IP_STATS_INC(ip.rterr);

  72. MIB2_STATS_INC(mib2.ipoutnoroutes);

  73. return NULL;

  74. }


  75. return netif_default;

  76. }

ip4_output_if函数

找到网卡之后就调用 ip4_output_if()函数将数据发送出去,这个函数会指定发送数据的网卡,同时会将来自上层协议(tcp、udp)的数据进行封装,组成IP数据报再发送,不过这个函数层层调用,比较麻烦,具体如下:。

  1. /**

  2. * 在网络接口上发送IP数据包。这个函数构造IP数据包首部并计算IP头校验和,

  3. * 如果源IP地址为NULL,在发送的时候就填写发送网卡的IP地址为源IP地址

  4. * 如果目标IP地址是LWIP_IP_HDRINCL,则假定pbuf已经存在包括IP头和有效负载指向它而不是数据。

  5. *

  6. * @param p 要发送的数据包(p->payload(有效负载)指向数据,如果dest == LWIP_IP_HDRINCL,则p已包含IP头和p->有效负载指向该IP头)

  7. * @param src 要发送的源IP地址(如果src == IP4_ADDR_ANY,则用发送的netif绑定的IP地址用作源地址)

  8. * @param dest 目的IP地址

  9. * @param ttl 要在IP标头中设置的TTL值(生存时间)

  10. * @param tos 用于在IP标头中设置的TOS值

  11. * @param proto 将在IP头中设置对应的上层协议

  12. * @param netif 发送此数据包的netif

  13. * @return ERR_OK 如果数据包发送正常就返回ok,

  14. * 如果p没有足够的空间用于IP /LINK标头,则为ERR_BUF

  15. * 其他则返回netif->output返回的错误

  16. *

  17. * @note ip_id:RFC791“某些主机可能只需使用

  18. * 独立于目的地的唯一标识符“

  19. */

  20. err_t

  21. ip4_output_if(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,

  22. u8_t ttl,u8_t tos,

  23. u8_t proto,struct netif *netif)

  24. {

  25. #if IP_OPTIONS_SEND

  26. return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL,0);

  27. }

  1. /**

  2. * 与ip_output_if()相同,但可以包含IP选项:

  3. *

  4. * @param ip_options指向IP选项的指针,复制到IP头中

  5. * @param optlen ip_options的长度

  6. */

  7. err_t

  8. ip4_output_if_opt(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,

  9. u8_t ttl,u8_t tos,u8_t proto,struct netif *netif,void*ip_options,

  10. u16_t optlen)

  11. {

  12. #endif /* IP_OPTIONS_SEND */

  13. constip4_addr_t*src_used = src;

  14. if(dest != LWIP_IP_HDRINCL){

  15. if(ip4_addr_isany(src)){

  16. src_used = netif_ip4_addr(netif);

  17. }

  18. }


  19. #if IP_OPTIONS_SEND

  20. return ip4_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif,

  21. ip_options, optlen);

  22. #else/* IP_OPTIONS_SEND */

  23. return ip4_output_if_src(p, src_used, dest, ttl, tos, proto, netif);

  24. #endif /* IP_OPTIONS_SEND */

  25. }

ip4_output_if_opt_src

首先看看这个函数到底做了什么吧:在上层协议递交数据包后,通过层层调用,最终到 ip4_output_if_opt_src()函数中处理,它的处理如下:图片代码的实现如下:注释非常丰富。主要过程就是:

  1. 判断是否填写好IP数据报首部?若目标IP地址为LWIPIPHDRINCL表示已经填写好IP数据报首部,且payload指针也指向了IP数据报首部。
  2. 如果没有填写IP数据报首部,调用 pbuf_add_header()函数调整数据区域指针以指向IP数据报首部。
  3. 填写IP数据报中的生存时间、服务类型、上层协议、目标IP地址、版本号与首部长度、数据报总长度、标志位和分片偏移量、标识、源IP地址等内容,总之就是将IP数据报首部的内容该填的都填上。
  4. 如果目标IP地址是自己的网卡IP地址,调用环回输入函数 netif_loop_output()发送IP数据报给自己,这种处理一般是用于测试代码。
  5. 如果IP数据报太大,数据报总长度大于网卡的MTU,则需要进行分片处理,调用 ip4_frag()函数进行发送。
  6. 直接调用注册的 netif->output接口传递给ARP,实际上就是调用 etharp_output()函数,在这里它会将IP地址解析成对应的 MAC地址,并且调用网卡发送函数进行发送。
  1. /**

  2. * 与ip4_output_if_opt()相同,当源地址是'IP4_ADDR_ANY'时,'src'地址不会被netif地址替换

  3. */

  4. err_t

  5. ip4_output_if_opt_src(struct pbuf *p,constip4_addr_t*src,constip4_addr_t*dest,

  6. u8_t ttl,u8_t tos,u8_t proto,struct netif *netif,void*ip_options,

  7. u16_t optlen)

  8. {

  9. #endif /* IP_OPTIONS_SEND */

  10. struct ip_hdr *iphdr;

  11. ip4_addr_t dest_addr;

  12. #if CHECKSUM_GEN_IP_INLINE

  13. u32_t chk_sum =0;

  14. #endif /* CHECKSUM_GEN_IP_INLINE */


  15. LWIP_ASSERT_CORE_LOCKED();

  16. LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);


  17. MIB2_STATS_INC(mib2.ipoutrequests);


  18. /* 应该要构建IP首部还是已经包含在pbuf中了?如果是要构建IP数据报首部 */

  19. if(dest != LWIP_IP_HDRINCL){

  20. u16_t ip_hlen = IP_HLEN;

  21. #if IP_OPTIONS_SEND

  22. u16_t optlen_aligned =0;

  23. if(optlen !=0){

  24. #if CHECKSUM_GEN_IP_INLINE

  25. int i;

  26. #endif /* CHECKSUM_GEN_IP_INLINE */

  27. if(optlen >(IP_HLEN_MAX - IP_HLEN)){

  28. /* 选项字段太长 */

  29. LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output_if_opt: optlen too long\\n"));

  30. IP_STATS_INC(ip.err);

  31. MIB2_STATS_INC(mib2.ipoutdiscards);

  32. return ERR_VAL;

  33. }

  34. /* 选项字段按照4字节对齐 */

  35. optlen_aligned =(u16_t)((optlen +3)&~3);

  36. ip_hlen =(u16_t)(ip_hlen + optlen_aligned);

  37. /* 首先写入IP选项字段 */

  38. if(pbuf_add_header(p, optlen_aligned)){

  39. LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output_if_opt: not enough room for IP options in pbuf\\n"));

  40. IP_STATS_INC(ip.err);

  41. MIB2_STATS_INC(mib2.ipoutdiscards);

  42. return ERR_BUF;

  43. }

  44. MEMCPY(p->payload, ip_options, optlen);

  45. if(optlen < optlen_aligned){

  46. /* 剩余字节清零 */

  47. memset(((char*)p->payload)+ optlen,0,(size_t)(optlen_aligned - optlen));

  48. }

  49. #if CHECKSUM_GEN_IP_INLINE

  50. for(i =0; i < optlen_aligned /2; i++){

  51. chk_sum +=((u16_t*)p->payload)[i];

  52. }

  53. #endif /* CHECKSUM_GEN_IP_INLINE */

  54. }

  55. #endif /* IP_OPTIONS_SEND */

  56. /* 生成IP头 */

  57. if(pbuf_add_header(p, IP_HLEN)){

  58. LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output: not enough room for IP header in pbuf\\n"));


  59. IP_STATS_INC(ip.err);

  60. MIB2_STATS_INC(mib2.ipoutdiscards);

  61. return ERR_BUF;

  62. }


  63. iphdr =(struct ip_hdr *)p->payload;

  64. LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",

  65. (p->len >=sizeof(struct ip_hdr)));


  66. IPH_TTL_SET(iphdr, ttl);

  67. IPH_PROTO_SET(iphdr, proto);

  68. #if CHECKSUM_GEN_IP_INLINE

  69. chk_sum += PP_NTOHS(proto |(ttl <<8));

  70. #endif /* CHECKSUM_GEN_IP_INLINE */


  71. /* 构建目的IP地址,此处的目的IP地址不能为NULL */

  72. ip4_addr_copy(iphdr->dest,*dest);

  73. #if CHECKSUM_GEN_IP_INLINE

  74. chk_sum += ip4_addr_get_u32(&iphdr->dest)&0xFFFF;

  75. chk_sum += ip4_addr_get_u32(&iphdr->dest)>>16;

  76. #endif /* CHECKSUM_GEN_IP_INLINE */


  77. IPH_VHL_SET(iphdr,4, ip_hlen /4);

  78. IPH_TOS_SET(iphdr, tos);

  79. #if CHECKSUM_GEN_IP_INLINE

  80. chk_sum += PP_NTOHS(tos |(iphdr->_v_hl <<8));

  81. #endif /* CHECKSUM_GEN_IP_INLINE */

  82. IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));

  83. #if CHECKSUM_GEN_IP_INLINE

  84. chk_sum += iphdr->_len;

  85. #endif /* CHECKSUM_GEN_IP_INLINE */

  86. IPH_OFFSET_SET(iphdr,0);

  87. IPH_ID_SET(iphdr, lwip_htons(ip_id));

  88. #if CHECKSUM_GEN_IP_INLINE

  89. chk_sum += iphdr->_id;

  90. #endif /* CHECKSUM_GEN_IP_INLINE */

  91. ++ip_id;


  92. if(src == NULL){

  93. ip4_addr_copy(iphdr->src,*IP4_ADDR_ANY4);/** 构建源IP地址 */

  94. }else{

  95. /* 此处的源IP地址不能为NULL */

  96. ip4_addr_copy(iphdr->src,*src);

  97. }


  98. #if CHECKSUM_GEN_IP_INLINE

  99. chk_sum += ip4_addr_get_u32(&iphdr->src)&0xFFFF;

  100. chk_sum += ip4_addr_get_u32(&iphdr->src)>>16;

  101. chk_sum =(chk_sum >>16)+(chk_sum &0xFFFF);

  102. chk_sum =(chk_sum >>16)+ chk_sum;

  103. chk_sum =~chk_sum;

  104. IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP){

  105. iphdr->_chksum =(u16_t)chk_sum;/* network order */

  106. }

  107. #if LWIP_CHECKSUM_CTRL_PER_NETIF

  108. else{

  109. IPH_CHKSUM_SET(iphdr,0);

  110. }

  111. #endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/

  112. #else/* CHECKSUM_GEN_IP_INLINE */

  113. IPH_CHKSUM_SET(iphdr,0);

  114. #if CHECKSUM_GEN_IP

  115. IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP){

  116. IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));

  117. }

  118. #endif /* CHECKSUM_GEN_IP */

  119. #endif /* CHECKSUM_GEN_IP_INLINE */

  120. }else{

  121. /* IP头已包含在pbuf中 */

  122. if(p->len < IP_HLEN){

  123. /** pbuf的长度小于IP数据报首部长度(20字节) */

  124. LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\\n"));

  125. IP_STATS_INC(ip.err);

  126. MIB2_STATS_INC(mib2.ipoutdiscards);

  127. return ERR_BUF;

  128. }

  129. iphdr =(struct ip_hdr *)p->payload;/** 直接从数据区域获取IP数据报首部 */

  130. ip4_addr_copy(dest_addr, iphdr->dest);/** 获取目的IP地址 */

  131. dest =&dest_addr;

  132. }


  133. IP_STATS_INC(ip.xmit);


  134. LWIP_DEBUGF(IP_DEBUG,("ip4_output_if: %c%c%"U16_F"\\n", netif->name[0], netif->name[1],(u16_t)netif->num));

  135. ip4_debug_print(p);


  136. #if ENABLE_LOOPBACK /** 换回接口 */

  137. if(ip4_addr_cmp(dest, netif_ip4_addr(netif))

  138. #if!LWIP_HAVE_LOOPIF

  139. || ip4_addr_isloopback(dest)

  140. #endif /* !LWIP_HAVE_LOOPIF */

  141. ){

  142. /* 数据包是给自己的,将其放入环回接口 */

  143. LWIP_DEBUGF(IP_DEBUG,("netif_loop_output()"));

  144. return netif_loop_output(netif, p);

  145. }

  146. #if LWIP_MULTICAST_TX_OPTIONS

  147. if((p->flags & PBUF_FLAG_MCASTLOOP)!=0){

  148. netif_loop_output(netif, p);

  149. }

  150. #endif /* LWIP_MULTICAST_TX_OPTIONS */

  151. #endif /* ENABLE_LOOPBACK */

  152. #if IP_FRAG

  153. /** 要发送的数据报大于mtu,需要分片,此处的前提是使能了IP_FRAG (IP分片) */

  154. if(netif->mtu &&(p->tot_len > netif->mtu)){

  155. return ip4_frag(p, netif, dest);/** 调用IP数据报分片函数将数据报分片发送出去 */

  156. }

  157. #endif /* IP_FRAG */


  158. LWIP_DEBUGF(IP_DEBUG,("ip4_output_if: call netif->output()\\n"));

  159. return netif->output(netif, p, dest);/** 如果不需要分片就直接通过网卡发送出去,netif->output() */

  160. }

最后提个醒

此外:上层协议是不会直接调用 ip4_output()函数的,lwip是通过宏定义将 ip4_output()函数进行重新定义:

  1. #define ip_output(p, src, dest, ttl, tos, proto) ip4_output(p, src, dest, ttl, tos, proto

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • IP协议
    +关注

    关注

    3

    文章

    80

    浏览量

    21547
  • 传输协议
    +关注

    关注

    0

    文章

    68

    浏览量

    11339
  • 网络层
    +关注

    关注

    0

    文章

    39

    浏览量

    10255
收藏 人收藏

    评论

    相关推荐

    深入了解示波器

    深入了解示波器
    发表于 11-14 22:32

    专家开讲:深入了解电池技术──Part 3

    资深工程师 Ivan Cowie 的「深入了解电池技术」专栏Part 3来啰!这次要介绍的是铅酸电池(lead-acidbatteries)技术。铅酸电池是在1859年由法国物理学家Gaston
    发表于 08-18 09:37

    单片机的深入了解

    项目名称:单片机的深入了解!项目是否开源:否申请开发板数量:1 块申请人团队介绍:我们团队由五个人组成,我们打算开始着手单片机的程序改编,设计一些比较特殊新颖的东西!希望给以支持!
    发表于 10-12 20:00

    深入了解LabVIEW FPGA资料分享

    深入了解LabVIEW FPGA
    发表于 05-27 08:35

    深入了解独立式数据采集系统CompactDAQ

    深入了解独立式数据采集系统CompactDAQ
    发表于 05-11 07:10

    请问网络中IP数据报传输的过程是怎样的?

    请问网络中IP数据报传输的过程是怎样的?
    发表于 10-27 07:03

    如何使用Winpcap接收和发送IP数据报文?

    如何使用Winpcap接收和发送IP数据报文?如何解决在Windows上配置双协议栈的一些问题?
    发表于 12-24 06:15

    介绍IP数据报的格式及IP相关功能在Lwip中的实现方式

    1、IP报文基础及其在Lwip的实现IP协议是TCP/IP协议中最为核心的协议,所有的TCP、UDP、ICMP及IGMP数据都已IP
    发表于 08-23 17:38

    示波器的深入了解

    示波器的深入了解 引言自然界运行着各种形式的正弦波,比如海浪、地震、声波、爆破、空气中传播的声音,或者身体运转的自然节律。物理世界里,能
    发表于 11-04 11:53 52次下载
    示波器的<b class='flag-5'>深入了解</b>

    带你深入了解光耦

    电子发烧友网带你深入了解光耦相关知识,讲述光耦的作用,光耦原理及各种光耦型号和替代型号,让大家全面了解光电耦合器
    发表于 03-16 16:43
    带你<b class='flag-5'>深入了解</b>光耦

    深入了解示波器入门手册

    深入了解示波器入门手册
    发表于 03-27 17:43 240次下载
    <b class='flag-5'>深入了解</b>示波器入门手册

    深入了解电感与磁珠的异同

    模拟电子的相关知识学习教材资料——深入了解电感与磁珠的异同
    发表于 09-27 15:19 0次下载

    带你深入了解示波器

    带你深入了解示波器
    发表于 02-07 14:26 18次下载

    深入了解安全光栅

    深入了解安全光栅
    的头像 发表于 06-25 13:53 764次阅读
    <b class='flag-5'>深入了解</b>安全光栅

    深入了解 GaN 技术

    深入了解 GaN 技术
    的头像 发表于 12-06 17:28 4503次阅读
    <b class='flag-5'>深入了解</b> GaN 技术