Linux网络编程之socket的各种结构

以下源码都在这几个源文件中

#include <sys/socket.h>	// 通用socket地址存放的地方
#include <linux/in.h>		// ipv4版本的socket地址存放的地方
#include <linux/in6.h>	// ipv6版本的socket地址存放的地方
#include <linux/un.h>		// unix版本的socket地址存放的地方

socket的通用地址

 struct socketaddr{
     sa_family_t sa_Family;           /** 指明socket所属的协议族类型,主要有3种
                                        * 地址族:AF_UNIX、          AF_INET、        AF_INET6
                                        * 协议族:PF_UNIX、          PF_INET、        PF_INET6
                                        * 描述 :UNIX本地域协议族、   TCP/IPv4协议族、  TCP/IPv6协议族
                                        * 他们3个的对应的地址族和协议族的值都是相等的,因此都是可以互换着用的
                                        */
     char sa_data[14];                /** 用于存放socket地址值,地址的长度也和协议族有关系
                                        * PF_UNIX:文件的路径名,长度可达108字节
                                        * PF_INET:16bit端口号和32bitIPv4地址,共6字节
                                        * PF_INET6: 16bit的端口号,32bit流标识,128bitIpv6地址,32bit范围ID,共26字节
                                        * 因此这里只有14字节的长度是不够使用的,后来使用下面那种
                                        */
 };


/**
 * 5.1.2 新的通用socket地址
 */
 struct socketaddr_storage{
     sa_family_t sa_family;
     unsigned long int __ss_align;
     char __ss_padding[128-sizeof(__ss_align)]; // 存放socket地址,总共120字节
     // 3种协议的地址值都可以存放,这里和源码不太一样,想要对齐128还需要减去sa_family的大小,最后应为118,最后数组大小应该为
     // char __ss_padding[128-sizeof(__ss_align)-sizeof(sa_family_t)]
 };

我们去看源代码

// 在socket.h中
// 下面这个sockaddr和上面的socketaddr是大致是一样的
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);	/* Common data: address family and length.  */
    char sa_data[14];		/* Address data.  */
  };

// 前往查看__SOCKADDR_COMMON,本质上是sa_family_t的别名,在预处理时会替换回去
#define	__SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family
// 前往查看sa_family_t,本质上是unsigned short int的别名,用数字代表某些地址族,和协议族相对应,在上面展示了主要是用的地址族和他们对应的协议族
typedef unsigned short int sa_family_t;

// 看第二个对应的socket通用类型的源码,
struct sockaddr_storage
  {
    __SOCKADDR_COMMON (ss_);	/* Address family, etc.  */
    char __ss_padding[_SS_PADSIZE];
    __ss_aligntype __ss_align;	/* Force desired alignment.  */
  };
// 第一个成员的和上面一样
// 第二个成员是一个char数组,大小为_SS_PADSIZE
#define _SS_PADSIZE \
  (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))

// 这3个都是别名,在预处理时会替换,我的机器上数组大小为120,
#define _SS_SIZE 128
#define __SOCKADDR_COMMON_SIZE	(sizeof (unsigned short int))
#define __ss_aligntype	unsigned long int

专用socket地址

所有专用socket地址类型变量在实际使用时都需要转化为通用的socket地址类型sockaddr, 因为所有socket编程接口都是使用的地址参数的类型都是sockaddr。
因此需要都小于118字节。

  1. IPv4协议族的专用socket地址
struct sockaddr_in {
  __kernel_sa_family_t	sin_family;	/* Address family		*/
  __be16		sin_port;	/* Port number			*/
  struct in_addr	sin_addr;	/* Internet address		*/

  /* Pad to size of `struct sockaddr'. */
  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) -
			sizeof(unsigned short int) - sizeof(struct in_addr)];
};

// 第一个就不多说
// 第二个参数是端口也就是unsigned short,为两个字节,16bit,对应tcp/ip报头的端口所占位数
typedef __u16 __bitwise __be16;
typedef unsigned short __u16;
// 第三个成员是ip地址,他是一个结构体,里面包含一个32位的unsigned int类型的成员,共4字节
struct in_addr {
	__be32	s_addr;
};
typedef __u32 __bitwise __be32;
typedef unsigned int __u32;
// 第四个成员是用来填充的,因为网络上都是用的通用socket,到解构时才根据对应的协议的专用socket进行解析具体信息,
// 因此需要专用的socket长度和通用相等,使得他们能相互转换,等到使用时在转换回来就行,因此这里需要将整个结构体填充成16字节,这里对应的是第一种通用结构体,128太大了浪费内存
#define __SOCK_SIZE__	16
  1. IPv6协议族的专用socket地址
// 总计大小为2+2+4+16+4=28字节
struct sockaddr_in6 {
	unsigned short int	sin6_family;    /* AF_INET6 */
	__be16			sin6_port;      /* Transport layer port # */
	__be32			sin6_flowinfo;  /* IPv6 flow information */
	struct in6_addr		sin6_addr;      /* IPv6 address */
	__u32			sin6_scope_id;  /* scope id (new in RFC2553) */
};
// 第一个成员类型:unsigned short int	大小为2字节,共16bit
// 第二个成员类型:unsigned short 大小为2字节,共16bit,符合报文中的首部的端口号大小格式
typedef __u16 __bitwise __be16;
typedef unsigned short __u16;
// 第三个成员类型: unsigned int 大小为4个字节,共32bit,这里应该是流标号,书上为20bit,这里用32bit,以实际为准
typedef __u32 __bitwise __be32;
typedef unsigned int __u32;
// 第四个成员为ipv6地址大小为128bit,也就是16字节
// 下面是一个union,每时每刻union中只有一个成员有值,这些成员的大小都是128bit因此union大小为128bit也就是16字节
struct in6_addr {
	union {
		__u8		u6_addr8[16];
#if __UAPI_DEF_IN6_ADDR_ALT
		__be16		u6_addr16[8];
		__be32		u6_addr32[4];
#endif
	} in6_u;
typedef unsigned char __u8;
typedef __u16 __bitwise __be16;
typedef unsigned short __u16;
typedef __u32 __bitwise __be32;
typedef unsigned int __u32;
// 第四个成员是范围ID,大小为32bit,共4字节,类型为unsigned int
typedef unsigned int __u32;
	
  1. UNIX协议族的专用socket地址,进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。
// 很简单,通过上面那些例子这个就很容易看懂了
struct sockaddr_un {
	__kernel_sa_family_t sun_family; /* AF_UNIX */
	char sun_path[UNIX_PATH_MAX];	/* pathname */
};
#define UNIX_PATH_MAX	108
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值