历史上的今天
返回首页

历史上的今天

今天是:2025年03月11日(星期二)

正在发生

2020年03月11日 | STM32开发笔记70: 传递参数对套接字地址进行强制类型转换

2020-03-11 来源:eefocus

单片机型号:STM32F407VGT6


在进行IPV6的UDP设计时,偶然发现一个问题,就是大部分套接字函数都需对地址进行强制转换,先看一下程序:


这是bind函数:


bind(sockIPV6, (struct sockaddr*)&sockAddr, sizeof(sockAddr))

这是recvfrom函数:


recvfrom(sockIPV6, UdpBuffer, 100, 0, (struct sockaddr*)&sockAddr, &slen)

这是sendto函数:


sendto(sockIPV6, UdpBuffer, len, 0, (const struct sockaddr*)&sockAddr, sizeof(sockAddr))

无一例外,这些函数在处理sockAddr之前都进行了强制数据类型转换,将其转换为sockaddr。


这个问题,在进行IPV4设计时,稀里糊涂的就过去了,没有深究过,今天在写IPV6时,疑惑就比较大了。


在IPV6中,sockAddr定义的数据类型是sockaddr_in6,在IPV4中定义的数据类型是sockaddr_in,为何能转换成同一数据类型呢?仔细分析后,发现里面有很大的玄机。


sockaddr_in的定义:


struct sockaddr_in {

  u8_t            sin_len;

  sa_family_t     sin_family;

  in_port_t       sin_port;

  struct in_addr  sin_addr;

#define SIN_ZERO_LEN 8

  char            sin_zero[SIN_ZERO_LEN];

};

sin_len:1字节,指明结构体有用数据的长度


sin_family:1字节,表示结构体的Family类型,指明是IPV4,还是IPV6


sin_port:2字节,端口号


sin_addr:4字节,IP地址


sin_zero:8字节,占位用


合计:16字节


sockaddr_in6的定义:


struct sockaddr_in6 {

  u8_t            sin6_len;      /* length of this structure    */

  sa_family_t     sin6_family;   /* AF_INET6                    */

  in_port_t       sin6_port;     /* Transport layer port #      */

  u32_t           sin6_flowinfo; /* IPv6 flow information       */

  struct in6_addr sin6_addr;     /* IPv6 address                */

  u32_t           sin6_scope_id; /* Set of interfaces for scope */

}

sin6_len:1字节,指明结构体有用数据的长度


sin6_family:1字节,表示结构体的Family类型,指明是IPV4,还是IPV6


sin6_port:2字节,端口号


sin6_flowinfo:4字节,包含IPV6报头中的通信流类别字段和流标签字段


sin6_addr:16字节,IPV6地址


sin6_scope_id:4字节,包含了范围ID,它用于标识一系列的接口,这些接口与地址字段中的地址相对应


合计:28字节


sockaddr的定义:


struct sockaddr {

  u8_t        sa_len;

  sa_family_t sa_family;

  char        sa_data[14];

};

sin_len:1字节,指明结构体有用数据的长度


sin_family:1字节,表示结构体的Family类型,指明是IPV4,还是IPV6


sa_data:14字节,占位用


合计:16字节


将sockaddr_in和sockaddr_in6转换为sockaddr是为保证代码的统一性,这样做后,socket中函数就可以采用统一的格式进行调用。


LwIP在进行初始设计时,本身不支持IPV6,所以将sockaddr_in和sockaddr定义为相同的长度。


在windows操作系统中,这3个结构体定义的长度是一致的的,都是28字节。


如果,这样问题又来了,在LwIP中sockaddr_in6和sockaddr长度不一致,是如何完成转换的呢?


我们那一个socket函数进行分析就好,例如我们选择bind函数,其内部定义如下:


int

lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)

{

  struct lwip_sock *sock;

  ip_addr_t local_addr;

  u16_t local_port;

  err_t err;

 

  sock = get_socket(s);

  if (!sock) {

    return -1;

  }

 

  if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {

    /* sockaddr does not match socket type (IPv4/IPv6) */

    sock_set_errno(sock, err_to_errno(ERR_VAL));

    return -1;

  }

 

  /* check size, family and alignment of 'name' */

  LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&

             IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),

             sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);

  LWIP_UNUSED_ARG(namelen);

 

  SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);

  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));

  ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);

  LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")n", local_port));

 

#if LWIP_IPV4 && LWIP_IPV6

  /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */

  if (IP_IS_V6_VAL(local_addr) && ip6_addr_isipv4mappedipv6(ip_2_ip6(&local_addr))) {

    unmap_ipv4_mapped_ipv6(ip_2_ip4(&local_addr), ip_2_ip6(&local_addr));

    IP_SET_TYPE_VAL(local_addr, IPADDR_TYPE_V4);

  }

#endif /* LWIP_IPV4 && LWIP_IPV6 */

 

  err = netconn_bind(sock->conn, &local_addr, local_port);

 

  if (err != ERR_OK) {

    LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%dn", s, err));

    sock_set_errno(sock, err_to_errno(err));

    return -1;

  }

 

  LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeededn", s));

  sock_set_errno(sock, 0);

  return 0;

}


可以看到,对于sockaddr其使用指针进行传递的。有关name的处理有很多函数,我们通过字面上理解,可定位于下面的宏。


SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);

继续找到其具体定义:


#define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))

再找到sockaddr_to_ipaddr_port的具体定义:


static void

sockaddr_to_ipaddr_port(const struct sockaddr* sockaddr, ip_addr_t* ipaddr, u16_t* port)

{

  if ((sockaddr->sa_family) == AF_INET6) {

    SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);

    ipaddr->type = IPADDR_TYPE_V6;

  } else {

    SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);

    ipaddr->type = IPADDR_TYPE_V4;

  }

}


到这里,我们就看的很清楚了,通过sockaddr_to_ipaddr_port函数,再强制转换为相应的结构体。

推荐阅读

史海拾趣

Anpec(茂达)公司的发展小趣事

随着技术的不断发展,茂达电子在产品研发上不断取得突破。公司投入大量人力、物力进行技术研发,并与多家国内外著名大学和研究机构合作,共同推进模拟集成电路设计的创新。经过不懈的努力,茂达电子成功推出了一系列世界领先的半导体器件产品,赢得了市场的广泛认可。

Agere System(LSI Logic)公司的发展小趣事

为了适应全球化的市场趋势,茂达电子制定了全面的全球化战略。公司不仅加强了与国际市场的联系,还积极寻求与国际知名企业的合作机会。同时,茂达电子还加大了对海外市场的投入,通过设立海外分支机构、参加国际展会等方式,不断提升品牌知名度和市场影响力。

这五个故事只是Anpec(茂达电子)发展历程中的一部分,但它们充分展示了茂达电子在电子行业中的崛起和成就。作为一家具有创新精神和市场洞察力的企业,茂达电子将继续致力于为客户提供优质的产品和服务,推动电子行业的持续发展。

Allianc公司的发展小趣事

为了进一步扩大市场份额和提升品牌影响力,Allianc公司开始实施国际化战略。公司积极开拓海外市场,与多家国际知名企业建立了合作关系。同时,公司还加强了对海外市场的调研和分析,针对不同地区的消费者需求推出了定制化产品。这些举措使得Allianc公司在国际市场上的份额不断攀升,品牌影响力也逐渐增强。

Elpress AB公司的发展小趣事

在追求经济效益的同时,Elpress AB也注重可持续发展和环境保护。公司积极采用环保材料和绿色生产工艺,降低生产过程中的能耗和排放。同时,Elpress AB还积极参与环保公益活动,推动电子行业的绿色发展。这些举措不仅体现了Elpress AB的社会责任感,也为公司的长期发展奠定了坚实的基础。

Advanced Detector Corp公司的发展小趣事

Advanced Detector Corp公司成立于上世纪80年代,由一群热衷于探测器技术研发的科学家和工程师创立。在创立初期,ADC便专注于开发高精度、高灵敏度的探测器技术,以满足当时日益增长的电子测量需求。公司通过持续的技术创新,逐渐在探测器领域取得了突破性的进展,并成功推出了一系列具有竞争力的产品。

场效应半导体(Cmos)公司的发展小趣事

在20世纪80年代初,随着计算机技术的飞速发展,对于高性能、低功耗的半导体器件需求日益增长。在这样的背景下,一家名为“先进微电子”(Advanced Micro Devices, AMD)的公司开始专注于CMOS技术的研发。AMD的工程师们通过不断的实验和优化,成功推出了首款高性能CMOS处理器,这款处理器以其卓越的性能和稳定性迅速获得了市场的认可。随着技术的不断进步,AMD在CMOS领域持续创新,逐步发展成为全球知名的半导体企业。

问答坊 | AI 解惑

最新无线收发模块资料总结

本帖最后由 paulhyde 于 2014-9-15 04:09 编辑 :P :P :P :P :P :P :P :P :P :P :P :P 好东西与各位分享!  …

查看全部问答>

请教各位WinCE/WinMobile上的security部分的csp和ssp[进者有分]

    大家都知道,ce和mobile上有很多的安全策略,可能比较常用的有模块验证(CertifyModule)。     请教大家个问题,安全策略中的csp和ssp最大的差别是什么,都用来做什么?能不能举一个例子来说明它们的典型应用?   & ...…

查看全部问答>

基于UCOS-II的串口程序问题

小弟最近正在研究基于UCOS-II的串口程序,开发环境是IAR,我将IAR自带的UART程序移植到UCOS-II下时发现有一些问题: 比如:在uart.h文件下 DWORD UARTInit( DWORD Baudrate ); __irq __nested __arm void UART0Handler( void ); void UARTSend( ...…

查看全部问答>

华为EM200模块通讯

可能是网络问题,在发送数据过程中连接会自动关闭,可是连接关闭后,服务器并没有自动断开(此时已经不能发送数据了) 为什么呀??如果有谁在做EM200通讯,请与我联系qq:273498325 不胜感激~~~~~…

查看全部问答>

sql ce与sqlserver 2000同步,困惑中......

正在开发ppc版的进销存和客户关系管理系统,对数据同步问题不明白:数据库的建立是在ppc端建立,还是在sqlserver 2000端建立?如果在ppc端建立,同步的时候sqlserver2000端能否自动建立?…

查看全部问答>

uclinux编译问题

今天成功运行了我烧出来的内核,给大家分享点资料。…

查看全部问答>

dsp28335地址线的问题

我用dsp外接一个处理芯片,想用读外接芯片片内的寄存器值,地址线连14根,0-9,12-15,dsp里配置引脚的话,这几根选择XA,其他的地址线没用到如10,11等引脚,配置成什么呢,pull up么? 还有就是我选用的14根地址线从地位到高位,是不是默认按数字 ...…

查看全部问答>

printf()肿么了

折腾完 notepad++加上 gcc编译器以后,很爽地在上边玩程序。可是,我发现一个问题,很要命的问题。那就是,程序里写的printf语句,程序运行时,printf内容的顺序不对头。比如说,应该先printf信息再按照信息输入的,结果它没printf出来,非要等到我 ...…

查看全部问答>

为什么整流桥中通过直流电压后压降比较大

由于我的电路中需要通过交直流两种,所以,电路前端用了整流桥KBP307。测量发现,当输入电压为24V,电流为0.5A的时候,压降有1.8V。这个压降太大了吧?这个什么原因呢? …

查看全部问答>

开关电源考试题和答案

开关电源考试题和答案…

查看全部问答>