Skip to content

netinet/in.h

头文件

cpp
#include <netinet/in.h>

地址结构体

  • IPv4 地址用结构体sockaddr_in表示,该结构体中包含 16 位的端口号和 32 位的 IP 地址;
  • IPv6 地址用结构体sockaddr_in6表示,该结构体中包含 16 位的端口号、128 位的 IP 地址和一些控制字段;
  • Unix Domain Socket 的地址格式定义在 sys/un.h 中,用结构体sock_addr_un表示。
cpp
struct sockaddr {
    __uint8_t       sa_len;         /* total length */
    sa_family_t     sa_family;      /* [XSI] address family */
    char            sa_data[14];    /* [XSI] addr value */
};

struct sockaddr_in {
    __uint8_t       sin_len;
    sa_family_t     sin_family;  // 协议族,同socket函数第一个参数,一般填 AF_INET
    in_port_t       sin_port;    // 2字节的端口
    struct in_addr  sin_addr;    // 4字节的ip地址
    char            sin_zero[8]; // 长度填充
};

// IP地址结构体
struct in_addr {
    in_addr_t s_addr;  // 4字节的ip地址
};

使用示例

cpp
// htons转为网络字节序
sin_port=hton(6632)

IP地址赋值

cpp
// 1、监听所有端口
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

// 2、监听指定端口
// 方式1,使用inet_addr 
// 只能用字符串格式的IP
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

// 方式2,使用gethostbyname
// 支持:域名/主机名/字符串格式的IP
#include <netdb.h>
struct hostent *host_net = gethostbyname("127.0.0.1");
memcpy(&socket_address.sin_addr, host_net->h_addr, host_net->h_length);

各种 socket 地址结构体的开头都是相同的,前 16 位表示整个结构体的长度(并不是所有 UNIX 的实现都有长度字段,如 Linux 就没有),后 16 位表示地址类型。

IPv4、IPv6 和 Unix Domain Socket 的地址类型分别定义为常数:

  • AF_INET
  • AF_INET6
  • AF_UNIX

这样,只要取得某种 sockaddr 结构体的首地址,不需要知道具体是哪种类型的 sockaddr 结构体,就可以根据地址类型字段确定结构体中的内容。

也因此,socket API 可以接受各种类型的 sockaddr 结构体指针做参数,例如 bind()、accept()、connect()等函数,这些函数的参数应该设计成void *类型以便接收各种类型的指针,但是 sock API 的实现早于 ANSI C 标准化,那时还没有void *类型,因此这些函数的参数都用struct sockaddr *类型表示,在传递参数之前要强制类型转换一下,例如:

cpp
#include <netinet/in.h>

struct sockaddr_in servaddr;
bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));