Linux socket地址结构体「DaemonCoder」

通用地址结构体

用socket进行网络编程时,首先需要知道要通信另一方的地址,linux中定义了sockaddr结构体来表示socket地址:

#inlcude <bits/socket.h>
struct sockaddr
{
    sa_family_t sa_family;
    char sa_data[14];
};
struct sockaddr_storage
{
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[128-sizeof(__ss_align)];
};

其中sa_family表示的是地址的协议族。创建socket时需要指定使用哪种协议族,常见的协议族以对应地址族的关系:

协议族

地址族

描述

地址示例、存储长度

PF_UNIX

AF_UNIX

UNIX本地域协议族

如 unix:/var/php-fpm.sock

文件的路径名,长度可达108字节

PF_INET

AF_INET

TCP/IPv4协议族

如 127.0.0.1:80

16bit端口号、32bit IPv4地址

PF_INET6

AF_INET6

TCP/IPv6协议族

如 [1fff:0:a88:85a3::ac1f]:8001

16bit端口号、32bit流标识、128bit IPv6地址、32bit范围ID,共26字节

上面 PF_* 和 AF_* 定义在 bits/socket.h 头文件中,前后两种宏有相同的值,通常是可以混用。

sockaddr.sa_data用来存放socket地址值,只有14字节,无法完全容纳多数协议族地址值,所以定义了上例中的第二个结构体:sockaddr_storage,__ss_padding成员用来存储地址值。

各协议族专用结构体

仔细看前面的通用地址结构体可以注意到,只有一个字段来存储地址值。像IPv4、IPv6协议地址中包括ip和端口号,都存储在一个字段中,每次使用还要来回解析,过于烦琐,所以Linux针对各个协议族定义了各自专用的socket地址结构体。

#include <sys/un.h>

// unix域
struct sockaddr_un
{
    sa_family_t sun_family;
    char sun_path[108];
};

// IPv4
struct sockaddr_in
{
    sa_family_t sin_family;     // 地址族:AF_INET
    u_int16_t sin_port;         // 端口号,用网络字节序表示
    struct in_addr sin_addr;    // IPv4地址结构体
};
struct in_addr
{
    u_int32_t s_addr;           // IPv4地址,用网络字节序表示
};

// IPv6
struct sockaddr_in6
{
    sa_family_t sin6_family;    // 地址族:AF_INET6
    u_int16_t sin6_port;        // 端口号,用网络字节序表示
    u_int32_t sin6_flowinfo;    // 流信息,应设置为0
    struct in6_addr sin6_addr;  // IPv6地址结构体
    u_int32_t sin6_scope_id;    // scope ID,尚处于实验阶段
};
struct in6_addr
{
    unsigned char sa_addr[16];  // IPv6地址,用网络字节序表示
};

因为socket相关接口使用的地址结构体是sockaddr,所以这些专用socket地址在使用时需要转成sockaddr类型,强制转换即可。

IP地址转换函数

#include <arpa/inet.h>

// IPv4函数
in_addr_t inet_addr(const char *strptr);
int inet_aton(const char *cp, struct in_addr *inp);
char* inet_ntoa(struct in_addr in);

// 同时适用于IPv4、IPv6地址函数
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);

示例:

#include <stdio.h>
#include <arpa/inet.h>

void main() {
    // inet_addr() 把点分十进制字符串形式的ip地址转换成网络字节序整数表示IPv4地址
    // 失败时返回返回 INADDR_NONE (-1)
    char *ip_str = "127.0.0.1";
    in_addr_t ip_int = inet_addr(ip_str);
    if (INADDR_NONE == ip_int) {
        printf("bad ip\n");
    } else {
        printf("%d\n", ip_int); // 16777343
    }

    // inet_aton() 把字符串ip转成in_addr类型的地址,第二个参数保存转换后的ip
    // 失败返回0,成功返回1
    struct in_addr ip_addr;
    int succ = inet_aton(ip_str, &ip_addr);
    if (0 == succ) {
        printf("bad ip\n");
    } else {
        printf("%d\n", ip_addr.s_addr); // 16777343
    }

    // 把in_addr类型的结构转成字符串ip
    char * ip_str2 = inet_ntoa(ip_addr);
    printf("%s\n", ip_str2); // 127.0.0.1
}

点击下方阅读原文,或访问 daemoncoder.com 发现更多优质内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值