Linux socket编程(二)——套接字地址结构

本文详细解析了套接字地址结构,包括通用、IPv4、IPv6的不同结构,以及字节排序和地址转换函数,是理解网络编程的基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我从不生产知识,我只是知识的搬运工 。

目录

1 套接字地址结构

1.1 通用套接字地址结构

1.2 IPv4套接字地址结构

1.3 IPv6套接字地址结构

1.4 新的通用套接字地址结构

1.5 套接字地址结构比较

2 字节排序函数

3 地址转换函数

3.1 IPv4地址转换函数

3.2 新的地址转换函数


 

1 套接字地址结构

        套接字地址结构与各个平台具体实现有关,以下给出linux-3.10.44下的实现;

1.1 通用套接字地址结构

         通用套接字定义在<sys/socket.h>中;

typedef unsigned short __kernel_sa_family_t;
typedef __kernel_sa_family_t	sa_family_t;

struct sockaddr {
	sa_family_t	sa_family;	/* address family, AF_xxx	*/
	char		sa_data[14];	/* 14 bytes of protocol address	*/
};

        套接字函数被定义为以指向某个通用套接字地址结构的一个指针作为其参数之一,即在调用套接字函数时,都将套接字(IPv4或IPv6地址结构)强制类型转换为通用套接字,然后作为参数传入函数;

struct sockaddr_in serv; /* IPv4 socket address structure */
bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));

1.2 IPv4套接字地址结构

        struct sockaddr_in 定义在in.h中;

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
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)];
};

1.3 IPv6套接字地址结构

        struct sockaddr_in6定义在in6.h中;

struct in6_addr {
	union {
		__u8		u6_addr8[16];
		__be16		u6_addr16[8];
		__be32		u6_addr32[4];
	} in6_u;
#define s6_addr			in6_u.u6_addr8
#define s6_addr16		in6_u.u6_addr16
#define s6_addr32		in6_u.u6_addr32
};

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) */
};

 

1.4 新的通用套接字地址结构

        相对比于struct sockaddr,struct sockaddr_storage有如下区别:

(1)、struct sockaddr_storage结构足以容纳系统所支持的任何套接字地址结构;

(2)、struct sockaddr_storage结构满足最苛刻的字节对齐要求;

#define _K_SS_MAXSIZE	128	/* Implementation specific max size */
#define _K_SS_ALIGNSIZE	(__alignof__ (struct sockaddr *))
				/* Implementation specific desired alignment */

typedef unsigned short __kernel_sa_family_t;

struct __kernel_sockaddr_storage {
	__kernel_sa_family_t	ss_family;		/* address family */
	/* Following field(s) are implementation specific */
	char		__data[_K_SS_MAXSIZE - sizeof(unsigned short)];
				/* space to achieve desired size, */
				/* _SS_MAXSIZE value minus size of ss_family */
} __attribute__ ((aligned(_K_SS_ALIGNSIZE)));	/* force desired alignment */

1.5 套接字地址结构比较

        这里参考《UNIX套接字编程卷一》给出BSD实现下的各个套接字地址结构的比较,只作参考;

 

2 字节排序函数

        大端:将高序字节存储在低位地址;

        小端:将低序字节存储在低位地址;

        为了方便操作,不去关心具体的转换细节,提供了几个字节排序函数用于主机字节序和网络字节序之间的转换;具体实现的时候可以使用主机字节序,而需要用网络协议传送字节时,再转为网络字节序;

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
                                        /*均返回:网络字节序的值*/
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
                                        /*均返回:主机字节序的值*/

        上述函数中,h代表host,n代表network,s代表short,l代表long;

 

3 地址转换函数

3.1 IPv4地址转换函数

        地址转换函数用于在ASCII字符串和网络字节序的二进制值之间转换网际地址;

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);
                /*返回:若字符串有效则为1, 否则为0*/
in_addr_t inet_addr(const char *cp);
                /*返回: 若字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE*/
char *inet_ntoa(struct in_addr in);
                /*返回:指向一个点分十进制数串的指针*/

        第一个函数inet_aton将cp所指的C字符串转换为一个32位的网络字节序二进制值;

        第二个函数inet_addr也是将字符串转换为网络地址,区别是该函数将所有地址都认为是有效地址(0.0.0.0~255.255.255.255),所以一些广播地址(如255.255.255.255)不能用该函数处理;所以该函数已被废弃,最好用inet_aton替换;

        第三个函数inet_ntoa将一个32位的网络地址转换为相应的点分十进制字符串;

3.2 新的地址转换函数

        上述函数只能处理IPv4地址,下文的新函数可以处理IPv4及IPv6,建议都使用以下函数进行地址转换;

#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
                    /*返回: 若成功返回1,若输入不是有效的表达式则为0,若出错则为-1*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
                    /*返回: 若成功则返回指向结果的指针,若出错返回NULL*/

        函数名中p和n分别代表表达式(presentation)和数值(numeric);两个函数的第一个参数af可以是AF_INET或AF_INET6;

        第一个函数尝试转换src指向的字符串,并通过指针dst存放二进制结果;

        第二个函数进行相反的转换,size参数指定目标存储单元的大小,以免该函数溢出其调用者的缓冲区,为了有助于指定这个大小,在inet.h中有如下定义;

#define INET_ADDRSTRLEN		(16)
#define INET6_ADDRSTRLEN	(48)

        如果len太小,不足以容纳表达式结果(包括结尾的空字符),将返回一个空指针,并置errno为ENOSPC;

        inet_ntop函数的参数dst不能为一个空指针,必须为目标存储单元分配内存并指定大小;
示例:

/*1. 字符串表达式 --> 二进制网络地址*/
char string[] = "192.168.1.1";
sockaddr_in addr;
inet_pton(AF_INET, string, &addr.sin_addr);

/*2. 二进制网络地址 --> 字符串表达式*/
char string[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &addr.sin_addr, string, sizeof(string));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值