先回顾一下, IPv4 socket 编程我们用到这两个数据结构:
struct sockaddr {
u_char sa_len; /* total length */
sa_family_t sa_family; /* address family */
char sa_data[14]; /* actually longer; address value */
};
struct sockaddr_in
{
UCHAR sin_len;
UCHAR sin_family;
VOS_UINT16 sin_port;
struct in_addr sin_addr;
CHAR sin_zero[8];
};
struct sockaddr 是一个通用的概念,表示任何socket address, connect,bind, recvfrom 等函数用的就是它; struct sockaddr_in 特别表示IPv4 internet address。 我们在做IPv4 编程的时候,其实脑子里已经确定了是在用IPv4的概念,初始化socket 的时候用的是IPv4, 然后把 sockaddr_in 转换为 sockaddr。 这是一个例子:
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: a.out " );
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err_sys("socket error");
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(12345); /* port */
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
err_quit("inet_pton error for %s", argv[1]);
if (connect(sockfd, (struct sockaddr *) & servaddr, sizeof(servaddr)) < 0)
err_sys("connect error");
我们看到, socket 函数决定了协议类型, 然后初始化 sockaddr_in 结构,接着 inet_pton 把IPv4地址的字符串表示转化为数字表示,这几步都是跟协议相关的。 到了connect 这步把sockaddr_in 转化为sockaddr 就可以了,因为它是跟协议无关的,接受的是sockaddr 参数。
显然,为了用IPv6, 我们需要一个新的数据结构来表示IPv6 地址,初始化这个结构,接下来就跟IPv4的socket编译差不多了:
struct sockaddr_in6 {
u_int8_t sin6_len; /* length of this struct(sa_family_t)*/
u_int8_t sin6_family; /* AF_INET6 (sa_family_t) */
u_int16_t sin6_port; /* Transport layer port # (in_port_t)*/
u_int32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
u_int32_t sin6_scope_id; /* scope zone index */
};
struct in6_addr {
union {
u_int8_t addr8[16];
u_int16_t addr16[8];
u_int32_t addr32[4];
} in6; /* 128-bit IP6 address */
};
其中 struct in6_addr 放的是128位IPv6地址。 这是代码片段:
/* generic function to create a socket */
int sockunion_stream_socket(union sockunion *unSu)
{
int intSock;
intSock = socket(unSu->su_family, SOCK_STREAM, 0);
if (intSock < 0)
{
printf("\r\n can't make socket hw_sockunion_stream_socket:%d", intSock);
}
return intSock;
}
void create_ipv6_socket(char *ip6str, unsigned short port, int family)
{
int ret;
union sockunion su;
int accept_sock;
struct in6_addr src_ip;
if (inet_pton(AF_INET6, ip6str, &src_ip) == 1)
{
printf("successfully convert ip6 address %s\n", ip6str);
}
else
{
printf("invalid ip6 address %s\n", ip6str);
return;
}
memset(&su, 0, sizeof (union sockunion));
su.su_family = (unsigned char)family;
/* Make new socket. */
accept_sock = sockunion_stream_socket(&su);
用到了一个辅助结构:
typedef union sockunion
{
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
}st_sockunion;
可以用来表示 sockaddr, sockaddr_in 或sockaddr_in6。