【Linux C | 网络编程】地址结构(sockaddr_in、sockaddr)、字节序转函数(htonl、htons、ntohl,ntohs)、IP转换函数

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:2024-01-29 20:30:30
⏰复习时间⏰:2024-02-27 11:35:15

本文未经允许,不得转发!!!


在这里插入图片描述

在这里插入图片描述

🎄一、概述

本文介绍网络编程的一些基础知识。在网络编程中,我们很多时候都需要准备地址结构的,下面先介绍三种需要用到的地址结构以及使用场景。然后介绍网络字节序相关的几个函数。最后再介绍IP地址转换的几个函数。这些知识是网络编程的必备基础知识。


在这里插入图片描述

🎄二、套接字地址结构

✨2.1 IPv4套接字地址结构

IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中。结构体定义如下:

#include <netinet/in.h>
struct sockaddr_in {
	sa_family_t    sin_family; /* address family: AF_INET */
	in_port_t      sin_port;   /* port in network byte order */
	struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
	uint32_t       s_addr;     /* address in network byte order */
};
  • sin_family:始终设置为AF_INET
  • sin_port:用网络字节序表示的端口。
  • sin_addr:是IP主机地址。结构in_addrs_addr成员是网络字节序表示的IP地址。in_addr应被分配一个INADDR_*值(例如,INADDR_ANY),或使用inet_aton、inet_addr、inet_makeaddr库函数进行设置。

使用场景:一般用来构造IPv4的地址结构,然后用于bind、connect等函数。


✨2.2 通用套接字地址结构

通用套接字地址结构以sockaddr 命名,定义在<sys/socket.h>头文件中。结构体定义如下:

#include <sys/socket.h>
struct sockaddr {
	sa_family_t sa_family;
	char        sa_data[14];
}
  • sin_family
    • AF_INET 表示 IPv4;
    • AF_INET6 表示IPv6;
    • AF_UNIX 表示unix;
    • AF_APPLETALK 表示ddp;
    • AF_PACKET 表示packet;
    • AF_X25 表示x25;
    • AF_NETLINK 表示netlink。
  • sa_data:协议指定的IP地址、端口等信息。

使用场景:一般不会定义实际变量,只用来做强制转换,将协议指定的套接字地址结构强制转换后传递给需要该地址的套接字函数(如:bind、connect、sendto等)。

使用struct sockaddr*作为传入参数的函数:bind、connect、sendto;作为传出参数的函数:accept、recvfrom、getsockname、getpeername

为什么需要强制转换?因为不同协议的地址结构不同,但套接字函数却只要一个,所以需要定义一个通用套接字地址结构指针作为参数来使用各个协议的结构体,所以将结构体地址传给套接字函数时需要强制转换成通用套接字地址结构指针(struct sockaddr*)。


✨2.3 IPv6套接字地址结构

IPv6套接字地址结构以sockaddr_in6 命名,定义在<netinet/in.h>头文件中。结构体定义如下:

#include <netinet/in.h>
struct sockaddr_in6 {
	sa_family_t     sin6_family;   /* AF_INET6 */
	in_port_t       sin6_port;     /* port number */
	uint32_t        sin6_flowinfo; /* IPv6 flow information */
	struct in6_addr sin6_addr;     /* IPv6 address */
	uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
};

struct in6_addr {
	unsigned char   s6_addr[16];   /* IPv6 address */
};

使用场景:一般用来构造IPv6的地址结构,然后用于bind、connect等函数。


在这里插入图片描述

🎄三、字节序转换函数

数据在内存中的存储有两种方式:

  • 小端字节序(little-endian):低位字节存储在低位地址;
  • 大端字节序(big-endian):低位字节存储在高位地址;

网际协议使用大端字节序来传输多字节整数,一般的Linux主机是小端字节序的。这就需要做转换,Linux编程时,可以使用下面4个函数做字节序转换:

#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表示hostn表示networkl表示longs表示short
htonl:将一个long型数据,从主机字节序转换到网络字节序;
htons:将一个short型数据,从主机字节序转换到网络字节序;
ntohl:将一个long型数据,从网络字节序转换到主机字节序;
ntohs:将一个short型数据,从网络字节序转换到主机字节序;

在这里插入图片描述

🎄四、IP地址转换函数

IPv4的IP地址转换函数:inet_aton、inet_addr、inet_ntoa
IPv6的IP地址转换函数:inet_pton、inet_ntop

✨4.1 inet_aton、inet_addr、inet_ntoa

inet_aton、inet_addr、inet_ntoa这几个函数可以将IPv4的IP地址在点分十进制字符串网络字节序两种形式之间相互转换。

#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
  • inet_aton:将C字符串形式IP地址转换为网络字节序形式的IP地址;

    • 参数:
      cp:传入参数,点分十进制的IP地址字符串;
      inp:传出参数,用来存储转换后的32位网络字节序IP地址的二进制值;
    • 返回值:字符串有效返回1,否则返回0;
  • inet_addr:将C字符串形式IP地址转换为网络字节序形式的IP地址;

    • 参数:
      cp:传入参数,点分十进制的IP地址字符串;
    • 返回值:字符串有效返回32位网络字节序IP地址的二进制值;无效返回INADDR_NONE(数值-1),该函数无法转换255.255.255.255,因为这个字符串返回也是-1。要避免这个问题的话,可以使用inet_aton
  • inet_ntoa:将一个32位的网络字节序二进制IPv4地址转换成相应的点分十进制数串。

    • 参数:
      in:传入参数,32位的网络字节序二进制IPv4地址;
    • 返回值:返回:指向一个点分十进制数串的指针

    注意,inet_ntoa返回值所指向的字符串驻留在静态内存中。如果其他线程也调用该函数,会修改字符串的值。

例子:

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

int main()
{
        unsigned int ip;
        inet_aton("192.168.1.100", (struct in_addr *)&ip);
        printf("inet_aton: ip=%x\n", ip);

        ip = inet_addr("192.168.1.100");
        printf("inet_addr: ip=%x\n", ip);

        struct in_addr ip_addr;
        ip_addr.s_addr = ip;
        char *ip_str = inet_ntoa(ip_addr);
        printf("inet_ntoa: ip=%s\n", ip_str);
        return 0;
}

运行结果:
在这里插入图片描述


✨4.2 inet_pton、inet_ntop

inet_pton、inet_ntop 是随IPv6出现的新函数,对于IPv4地址和IPv6地址都适用。函数名中pn分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构中的二进制值。

#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • inet_pton:将src指向的C字符串形式IP地址转换为数值形式的IP地址并存放在dst的内存;
    • 参数:
      af:表示地址协议,可以是AF_INET(IPv4),也可以是AF_INET6(IPv6);
      src:传入参数,点分十进制的IP地址字符串;
      dst:传出参数,用来存储转换后的数值形式IP地址结果;
    • 返回值:若成功则为1,若输入不是有效的表达格式则为0,若出错则为-1。
  • inet_ntop:将src指向的数值形式的IP地址转换为C字符串形式IP地址并存放在dst的内存;
    • 参数:
      af:表示地址协议,可以是AF_INET(IPv4),也可以是AF_INET6(IPv6);
      src:传入参数,数值形式IP地址;
      dst:传出参数,用来存储转换后的点分十进制字符串结果;
      size:是dst指向的内存的大小,以免发生溢出。如果size太小,不足以容纳表达格式结果(包括结尾的空字符),那么返回一个空指针,并置errno为ENOSPC。size的取值参考下面两个宏定义。
      #include <netinet/in.h>
      #define INET_ADDRSTRLEN 16
      #define INET6_ADDRSTRLEN 46
      
    • 返回值:若成功则为指向结果的指针,若出错则为NULL。

例子:

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

int main()
{
	int i = 0;
	struct sockaddr_in6 inet6_addr;
	inet_pton(AF_INET6, "fe80::20c:29ff:fe26:c844", &inet6_addr.sin6_addr);
	printf("inet_pton: inet6=");
	for(i=0; i<sizeof(inet6_addr.sin6_addr.s6_addr); i++)
	{
		printf("%x", inet6_addr.sin6_addr.s6_addr[i]);
	}
	printf("\n");

	char inet6_str[INET6_ADDRSTRLEN+1]={0,};
	const char *ip_str = inet_ntop(AF_INET6, &inet6_addr.sin6_addr, inet6_str, sizeof(inet6_str));
	printf("inet_ntop: inet6_str=%s\n", ip_str);
	return 0;
}

运行结果:
在这里插入图片描述


在这里插入图片描述

🎄五、总结

👉本文详细介绍Linux系统网络编程的基础知识,包括套接字地址结构(sockaddr_in、sockaddr、sockaddr_in6)、字节序转函数(htonl、htons、ntohl,ntohs)、IP转换函数(inet_aton、inet_addr、inet_ntoa、inet_pton、inet_ntop)。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wkd_007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值