本文转自:https://blog.csdn.net/albertsh/article/details/80991684

前言
最近在学习网络相关的知识,虽然之前代码写了不少,但是长时间不写难免会忘记,简单地复习了一下IO多路复用的方式,对比了解了一下epoll模式和select模式的异同,不过写代码的时候发现,这个socket连接中有几个结构还是挺让人头大的,用着用着突然就强转成其他的类型了,加上年前改了半天IPv6的连接,这几个结构体更加混乱,所以今天角色放到一起,从源码的角度看一下sockaddr、sockaddr_in、sockaddr_in6这三个结构体之间的联系,以及为什么有些情况可以直接强转。

代码分析
看一下这三个结构的定义,先说明一下版本,操作系统为CentOS,头文件版本应该挺古老了,在’/usr/include/netinet/in.h’ 中发现版权信息:Copyright (C) 1991, 1992, 1994-2001, 2004, 2006, 2007, 2008, 2009, 2010,看着很古老,但之后的版本应该没有改动很大吧,反正不太清楚,我们就分析当前这一个版本吧。

/* /usr/include/bits/socket.h */
/* Structure describing a generic socket address.  */
struct sockaddr
{__SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */char sa_data[14];           /* Address data.  */
};/* /usr/include/netinet/in.h */
/* Structure describing an Internet socket address.  */
struct sockaddr_in
{__SOCKADDR_COMMON (sin_);in_port_t sin_port;         /* Port number.  */struct in_addr sin_addr;    /* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];
};/* /usr/include/netinet/in.h */#ifndef __USE_KERNEL_IPV6_DEFS/* Ditto, for IPv6.  */
struct sockaddr_in6
{__SOCKADDR_COMMON (sin6_);in_port_t sin6_port;        /* Transport layer port # */uint32_t sin6_flowinfo;     /* IPv6 flow information */struct in6_addr sin6_addr;  /* IPv6 address */uint32_t sin6_scope_id;     /* IPv6 scope-id */
};#endif /* !__USE_KERNEL_IPV6_DEFS */

看到3个结构的定义想到了什么?只是看着有点像吧,真正的区别我们往下看,其中3个结构里都包含了 __SOCKADDR_COMMON 这个宏,我们先把它的定义找到,最后在’usr/inlcue/bits/sockaddr.h’中找到如下代码,

/* POSIX.1g specifies this type name for the `sa_family' member.  */
typedef unsigned short int sa_family_t;/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc.  */#define __SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##family#define __SOCKADDR_COMMON_SIZE  (sizeof (unsigned short int))

由此我们知道,这三个结构的第一个字段都是一个unsigned short int 类型,只不过用宏来定义了三个不同的名字,至此第一个结构就清楚了,在一般环境下(short一般为2个字节),整个结构占用16个字节,变量sa_family占用2个字节,变量sa_data 保留14个字节用于保存IP地址信息。

接着我们发现第二个结构中还有in_port_t和struct in_addr两个类型没有定义,继续找下去吧,在文件
‘/usr/include/netinet/in.h’发现以下定义

/* Type to represent a port.  */
typedef uint16_t in_port_t;/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
};

这么看来sockaddr_in这个结构也不复杂,除了一开始的2个字节表示sin_family,然后是2个字节的变量sin_port表示端口,接着是4个字节的变量sin_addr表示IP地址,最后是8个字节变量sin_zero填充尾部,用来与结构sockaddr对齐

现在我们该分析结构sockaddr_in6了,这里边只有一个未知的结构in6_addr,经过寻找发现其定义也在’/usr/include/netinet/in.h’中

#ifndef __USE_KERNEL_IPV6_DEFS/* IPv6 address */
struct in6_addr
{union{uint8_t __u6_addr8[16];#if defined __USE_MISC || defined __USE_GNUuint16_t __u6_addr16[8];uint32_t __u6_addr32[4];#endif} __in6_u;#define s6_addr         __in6_u.__u6_addr8#if defined __USE_MISC || defined __USE_GNU# define s6_addr16      __in6_u.__u6_addr16# define s6_addr32      __in6_u.__u6_addr32#endif};#endif /* !__USE_KERNEL_IPV6_DEFS */

这个结构看起来有点乱,但是如果抛开其中的预编译选项,其实就是8个字节,用来表示IPV6版本的IP地址,一共128位,只不过划分字节的段数有些不同,每段字节多一点那么段数就少一点,反义亦然。

那接下来我们整理一下,为了看的清楚,部分结构使用伪代码,不能通过编译,主要是方便对比,整理如下

/* Structure describing a generic socket address.  */
struct sockaddr
{uint16 sa_family;           /* Common data: address family and length.  */char sa_data[14];           /* Address data.  */
};/* Structure describing an Internet socket address.  */
struct sockaddr_in
{uint16 sin_family;          /* Address family AF_INET */ uint16 sin_port;            /* Port number.  */uint32 sin_addr.s_addr;     /* Internet address.  */unsigned char sin_zero[8];  /* Pad to size of `struct sockaddr'.  */
};/* Ditto, for IPv6.  */
struct sockaddr_in6
{uint16 sin6_family;         /* Address family AF_INET6 */uint16 sin6_port;           /* Transport layer port # */uint32 sin6_flowinfo;       /* IPv6 flow information */uint8  sin6_addr[16];       /* IPv6 address */uint32 sin6_scope_id;       /* IPv6 scope-id */
};

这么来看是不是就清晰多了,由此我们发现结构 sockaddr 和 sockaddr_in 字节数完全相同,都是16个字节,所以可以直接强转,但是结构 sockaddr_in6 有28个字节,为什么在使用的时候也是直接将地址强制转化成(sockaddr*)类型呢?

强转的可能性
其实sockaddr 和 sockaddr_in 之间的转化很容易理解,因为他们开头一样,内存大小也一样,但是sockaddr和sockaddr_in6之间的转换就有点让人搞不懂了,其实你有可能被结构所占的内存迷惑了,这几个结构在作为参数时基本上都是以指针的形式传入的,我们拿函数bind()为例,这个函数一共接收三个参数,第一个为监听的文件描述符,第二个参数是sockaddr*类型,第三个参数是传入指针原结构的内存大小,所以有了后两个信息,无所谓原结构怎么变化,因为他们的头都是一样的,也就是uint16 sa_family,那么我们也能根据这个头做处理,原本我没有看过bind()函数的源代码,但是可以猜一下:

int bind(int socket_fd, sockaddr* p_addr, int add_size)
{if (p_addr->sa_family == AF_INET){sockaddr_in* p_addr_in = (sockaddr_in*)p_addr;//...}else if (p_addr->sa_family == AF_INET6){sockaddr_in6* p_addr_in = (sockaddr_in6*)p_addr;//...}else{//...}
}

由以上代码完全可以实现IPv4和IPv6的版本区分,所以不需要纠结内存大小的不同

总结
通过等价替换的方式我们可以更好的了解sockaddr、sockaddr_in、sockaddr_in6之间的异同。
网路接口函数针对于IPv4和IPv6虽然有不同的结构,但是接口基本相同,主要是为了用户(开发者)使用方便吧。
有时间可以看一下bind()、accept()等函数,看看其中对于结构的使用到底是怎样的。

sockaddr、sockaddr_in、sockaddr_in6的区别及转换相关推荐

  1. JQuery对象和JS对象区别与转换|| 事件绑定 入口函数 样式控制

    JQuery对象和JS对象区别与转换   1. JQuery对象在操作时,更加方便.     2. JQuery对象和js对象方法不通用的.     3. 两者相互转换         * jq -- ...

  2. CString与string、char*的区别和转换

    我们在C++的开发中经常会碰到string.char*以及CString,这三种都表示字符串类型,有很多相似又不同的地方,常常让人混淆.下面详细介绍这三者的区别.联系和转换: 各自的区别 char*: ...

  3. java与jquery的选择器区别_java day44【JQuery 基础:概念,快速入门,JQuery对象和JS对象区别与转换,选择器,DOM操作,案例】...

    第一章JQuery 基础 1. 概念: 一个JavaScript框架.简化JS开发 * jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScrip ...

  4. Python3列表、元组及之间的区别和转换

    文章目录 1. 列表(list) 1.1 列表创建.切片.删除.检索 1.2 列表常用函数 2. 元组(tuple) 3. 列表与元组区别及转换 1. 列表(list) 1.1 列表创建.切片.删除. ...

  5. python数字类型floatcomplexint_Python 四种数值类型(int,long,float,complex)区别及转换

    对python这个高级语言感兴趣的小伙伴,下面一起跟随512笔记的小编两巴掌来看看吧! Python支持四种不同的数值类型,包括int(整数)long(长整数)float(浮点实际值)complex ...

  6. java day44【JQuery 基础:概念,快速入门,JQuery对象和JS对象区别与转换,选择器,DOM操作,案例】...

    第一章  JQuery 基础 1. 概念: 一个JavaScript框架.简化JS开发 * jQuery是一个快速.简洁的JavaScript框架,是继Prototype之后又一个优秀的JavaScr ...

  7. CDC和HDC的区别与转换

    CDC和HDC的区别与转换 一.区别与联系 HDC是句柄:CDC是MFC封装的Windows   设备相关的一个类:CClientDC是CDC的衍生类,产生对应于Windows客户区的对象 HDC是W ...

  8. 【JQuery框架】JQuery对象和JS对象的区别和转换

    目录 jQuery的概念 jQuery快速入门 1.下载jQuery 2.导入JQuery的js文件 3.jQuery的使用 jQuery对象和JS对象区别与转换 jQuery转为js js转为jQu ...

  9. day43 JavaWen阶段——JQuery 基础(JQuery相关开发文档,JQuery对象和JS对象区别与转换,JQuery选择器,JQuery中DOM操作,案列【QQ表情选择】【左右移动】)

    今日内容 1. JQuery 基础: 概念 快速入门 JQuery对象和JS对象区别与转换 选择器 DOM操作 案例 今日源码: 链接:https://pan.baidu.com/s/1KiG0c_V ...

最新文章

  1. 如何设置MathType下标的正斜体
  2. 由“ASP.NET网站限制访问频率”想到的两点问题(转)
  3. win7(x64)安装oracle 10g 32位的方法
  4. 在Opendaylight中karaf启动的时候自动安装feature
  5. JDBC笔记02-数据库连接池 Spring JDBC
  6. 利用.dSYM和.app文件准确定位Crash位置
  7. 烟花程序c语言,C语言烟花程序
  8. 在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许...
  9. python将图片转换为Framebuffer裸数据格式(终端显示图片)
  10. 希尔排序 最坏时间_算法篇----希尔排序
  11. URL中文传值乱码解决方式
  12. Mellanox infinoband RDMA SDP
  13. 计算机文化基础清华大学出版社,清华大学出版社-图书详情-《计算机基础》
  14. 很恶心的一个关于字符串的题目!
  15. 信息与计算科学偏计算机的学校,信息与计算科学:披着计算机“外衣”的数学专业...
  16. 计算机iq测试,超大多数人类 瑞典天才电脑智商测试得分150
  17. 网站卡顿,推荐这些免费使用加速工具的网站
  18. linux fat32转ntfs,fat32怎么转换ntfs格式?不损坏数据FAT32转NTFS命令是什么 电脑维修技术网...
  19. 分享一些光纤模块接口类型有用信息给大家
  20. 关于test eax eax

热门文章

  1. 不同电脑上提交 gerrit
  2. wps js窗体的创建
  3. tabindex属性介绍,让普通元素能够执行聚焦(foucs)和失焦(blur)事件
  4. TH库学习(二): THTensorApply宏观理解(简化)
  5. Vue项目中安装使用axios
  6. 机器学习--单细胞聚类(一)
  7. Office WPS PPT如何微量调整文本框的位置
  8. 用java写爱情,既然编制了一个梦,那就圆了它。
  9. 《我曾》火了:人这辈子,最怕突然听懂这首歌
  10. 2022-2028全球与中国数码喷墨打印机市场现状及未来发展趋势