sockaddr、sockaddr_un、sockaddr_in、sockaddr_in6、struct in_addr、struct in6_addr的区别与使用

本文详细介绍了Linux下socket编程中的多种地址结构体,包括通用的sockaddr、用于本地通信的sockaddr_un、IPv4专用的sockaddr_in、IPv6专用的sockaddr_in6以及存储IP地址的struct in_addr和struct in6_addr。

sock_addr


/* Linux下定义在usr/include/bits/socket.h中 */
/* Structure describing a generic socket address. */
struct sockaddr
{
	uint16_t sa_family; /* Comm data: address family and length. */
	char sa_data[14]; /* Address data. */
};

在socket套接字编程中,sockaddr是一个通用的结构体,可以存储不同类型的地址结构。sa_family表示地址族,AF_INET表示IPv4,AF_INET6表示IPv6,sa_data包含套接字中的目标地址和端口信息。但是这个结构体通常只用来提供统一接口可以被bind()、connect()等函数调用,而不直接对结构体操作。


sockaddr_un


/* Linux下定义在usr/include/sys/un.h中 */
/* Structure describing the address of an AF_LOCAL (aka AF_UNIX) socket. */
struct sockaddr_un
{
	uint16_t sun_family;
	char sun_path[108]; /* Path name. */
};

sockaddr_un是一种UNIX套接字,通常在使用这种方式时不用网络套接字,而是用本地套接字,这种方式可以避免为黑客留下后门(据说~)。本地套接字与网络套接字的使用大体相同,主要区别在于使用socket()创建时,域参数只能选择PF_LOCAL和PF_UNIX,通讯类型只能选择SOCK_STREAM和SOCK_DGRAM,协议为默认协议;bind()绑定时填充的是sockaddr_un结构体,它的sun_family参数只能选择AF_LOCAL和AF_UNIX,sun_path参数为本地文件路径,通常放在/temp目录下。


sockaddr_in


/* Linux下定义在usr/include/netinet/in.h中 */
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
	uint16_t sin_family;
	uint16_t sin_port;			/* Port number. */
	struct in_addr sin_addr;	/* Internet address. */

	/* Pad to size of 'struct sockaddr'. */
	unsigned char sin_zero[sizeof (struct sockaddr) -
						   sizeof (uint16_t) - 
						   sizeof (uint16_t) -
						   sizeof (struct in_addr)];
	/* 其实就是unsigned char sin_zero[8]; */
};

sockaddr_in结构体是和sockaddr结构并列且等价的结构体,因此它们二者之间可以互相转化,也是真正用来提供给程序员进行填充操作的结构体,区别在于sockaddr_in将sa_data划分为sin_port和sin_addr,也就是把端口和IP地址信息区分开,sin_port采用了网络字节序,同时为了保持和sockaddr相同的字节大小,填充了8字节的sin_zero。这也是一个只针对IPv4地址的结构体,因此它的sin_family只能是AF_INET。


sockaddr_in6


/* Linux下跟sockaddr_in在同一个头文件 */
/* Ditto, for IPv6. */
struct sockaddr_in6
{
	uint16_t sin6_family;
	uint16_t sin6_port;			/* Transport layer port # */
	uint32_t sin6_flowinfo;		/* IPv6 flow information */
	struct in6_addr sin6_addr;	/* IPv6 address */
	uint32_t sin6_scope_id;		/* IPv6 scope-id */
};

sockaddr_in6也是一个专用的结构体,只针对IPv6,sin6_family参数只能选择AF_INET6,sin6_port也采用了网络字节序,sin6_flowinfo分为两个字段,低序的20位表示流标(flow label),高序的12位保留,对于具备范围的地址(scoped address),sin6_scope_id标识了其范围(scope)。


struct in_addr


/* Linux下与sockaddr_in在同一个头文件 */
/* Internet address. */
struct in_addr
{
	uint32_t s_addr;
};

这个结构体在sockaddr_in中被使用来存储IP地址信息,因此它通常不单独使用,而是与sockaddr_in结合一起使用,它的结构比较简单,就是采用了4字节存储IP地址,不过也采用了网络字节序进行存储,使用时需要注意转换。


struct in6_addr


/* Linux下与sockaddr_in在同一个头文件 */
/* IPv6 address */
struct in6_addr
{
	union
	{
		uint8_t __u6_addr8[16];
		uint16_t __u6_addr16[8];
		uint32_t __u6_addr32[4];
	}__in6_u;
#define s6_addr		__in6_u.__u6_addr8
#ifdef __USE_MISC
#define s6_addr16 	__in6_u.__u6_addr16
#define s6_addr32 	__in6_u.__u6_addr32
#endif
};

这个结构体也是与sockaddr_in6配合起来一起使用,通常不单独使用。它的结构比in_addr看起来稍微复杂,不过就是增加了一些预编译命令,它的主体是一个联合体,大小为16字节,因为IPv6地址长度是128位,刚好16字节,它使用联合体提供了几种不同的存储形式,为程序员提供了不同操作的可能。


`struct sockaddr_in` 是在网络编程中用于存储 IPv4 地址信息的结构体,其定义如下: ```c struct sockaddr_in { // 地址族,通常为 AF_INET 表示 IPv4 sa_family_t sin_family; // 端口号,需要使用 htons() 函数进行字节序转换 in_port_t sin_port; // IPv4 地址,使用 in_addr 结构体表示 struct in_addr sin_addr; // 填充字节,使 sockaddr_in sockaddr 大小相同 char sin_zero[8]; }; ``` `struct in_addr` 结构体用于存储 IPv4 地址,其定义如下: ```c struct in_addr { // 32 位的 IPv4 地址 in_addr_t s_addr; }; ``` ### 使用场景 `struct sockaddr_in` 类型的变量在网络编程中主要用于以下场景: - **服务器端**:在创建服务器套接字时,需要使用 `struct sockaddr_in` 来绑定服务器的 IP 地址和端口号。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> int main() { int server_fd, new_socket; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buffer[1025] = {0}; const char *hello = "Hello from server"; // 创建套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字选项 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(8080); // 绑定套接字到指定地址和端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } // 接受连接 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } // 读取客户端发送的数据 read(new_socket, buffer, 1024); printf("Client: %s\n", buffer); // 发送响应给客户端 send(new_socket, hello, strlen(hello), 0); printf("Hello message sent\n"); // 关闭套接字 close(new_socket); close(server_fd); return 0; } ``` - **客户端**:在连接服务器时,需要使用 `struct sockaddr_in` 来指定服务器的 IP 地址和端口号。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> int main() { int sock = 0; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[1025] = {0}; // 创建套接字 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n"); return -1; } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(8080); // 将 IPv4 地址从文本转换为二进制形式 if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { printf("\nInvalid address/ Address not supported \n"); return -1; } // 连接到服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\nConnection Failed \n"); return -1; } // 发送数据到服务器 send(sock, hello, strlen(hello), 0); printf("Hello message sent\n"); // 读取服务器响应 read(sock, buffer, 1024); printf("Server: %s\n", buffer); // 关闭套接字 close(sock); return 0; } ``` ### 作用 `struct sockaddr_in` 的主要作用是存储和传递 IPv4 地址和端口号信息,方便在网络编程中进行地址绑定、连接等操作。在使用 `bind()`、`connect()`、`accept()` 等函数时,需要将 `struct sockaddr_in` 类型的变量强制转换为 `struct sockaddr` 类型,因为这些函数的参数类型是 `struct sockaddr *`。为了减少强制转换的代码,可以使用共用体来简化操作,示例如下: ```c typedef union { struct sockaddr *saddr; struct sockaddr_in *inaddr; } self_sockaddr_type; struct sockaddr_in client_addr; client_addr.sin_family = AF_INET; client_addr.sin_addr.s_addr = htonl(INADDR_ANY); client_addr.sin_port = htons(13400); self_sockaddr_type tmp; tmp.inaddr = &client_addr; // 使用自定义共用体类型保存 struct sockaddr_in 变量地址 int ret = connect(fd, tmp.saddr /*无需强转*/, sizeof(struct sockaddr_in)); ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

# 不想写代码的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值