linux socket hton innet_pton


原文链接: linux socket hton innet_pton

(1条消息)sockaddr_in与sockaddr的区别,以及对网络字节序和主机字节序的理解和转换函数_ZWE7616175的博客-CSDN博客_sockaddr_insockaddr
我们使用socket进行通信的时候,我们需要指定三个元素:通信域(地址族)、IP地址、端口号,这三个元素由SOCKADDR_IN结构体定义,为了简化编程一般将IP地址设置为INADDR_ANY,如果需要使用特定的IP地址则需要使用inet_addr 和inet_ntoa完成字符串和in_addr结构体的互换,in_addr是SOCKADDR_IN成员,其代表IP地址。

一、sockaddr
sockaddr在/usr/include/bits/socket.h下,查看sockaddr的结构:

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.     协议族 */
    char sa_data[14];           /* Address data.  地址+端口号*/
  };

sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了。而sockaddr_in就解决了这一缺陷,将端口号和IP地址分开存储。

二、sockaddr_in
sockaddr_in在/usr/include/netinet/in.h下,
查看sockaddr_in的结构:

typedef uint32_t in_addr_t;
 struct in_addr
 {
     in_addr_t s_addr;       //32位IPV4地址
  };
struct sockaddr_in
   {
     __SOCKADDR_COMMON (sin_);/* Common data: address family and length.     协议族 */
    in_port_t sin_port;                    /* Port number.  16位端口号*/
    struct in_addr sin_addr;            /* Internet address.  32位IP地址*/

     /* Pad to size of `struct sockaddr'.  用于填充的0字节*/
    unsigned char sin_zero[sizeof (struct sockaddr) -
                            __SOCKADDR_COMMON_SIZE -
                            sizeof (in_port_t) -
                           sizeof (struct in_addr)];
   };


三、两者之间的区别与联系
我们来看一下sockaddr_in与sockaddr的结构:

联系:二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别。
区别:sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。
而sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作。使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。
————————————————
版权声明:本文为CSDN博主「ZWE7616175」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ZWE7616175/article/details/80252048

unsigned long inet_addr(const char* cp);

cp代表点分十进制的IP地址,如"1.2.3.4"

struct sockaddr_in local_addr, remote_addr;

int slen = sizeof(remote_addr);
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(port);//htons(atoi(port));
remote_addr.sin_addr.s_addr = inet_addr("192.168.1.111");
memset(remote_addr.sin_zero, '\0', sizeof(remote_addr.sin_zero));
bzero(&(remote_addr.sin_zero), 8); 

函数使用代码如下:
char* inet_ntoa(struct in_addr in);
参数:
    in代码in_addr的结构体,其结构体如下:

//将in_addr类型转换为字符串
printf("inet_ntoa ip = %s\n",inet_ntoa(sock.sin_addr));

printf("\nrecv from ip = %s port = %d to ip = %s port = %d msg > %s\n", inet_ntoa(si.sin_addr), ntohs(si.sin_port), inet_ntoa(si2.sin_addr), ntohs(si2.sin_port), buf);

结果输出:
inet_ntoa ip = 192.168.1.111
————————————————
版权声明:本文为CSDN博主「Jimmy1224」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/c_base_jin/article/details/60469622

网络编程inet_pton inet_ntop inet_addr htonl htons ntohl ntohs
把给定系统所采用的字节序成为主机字节序,为了避免不同类型主机之间在数据交换的时候育有对于字节序的不同而导致的差错,引入网络字节序。

例一:IP地址的主机字节序与网络字节序的转换以用IP地址:127.0.0.1为例
1
第一步 127 . 0 . 0 . 1 把IP地址每一部分转换为8位的二进制数。
第二步 01111111 00000000 00000000 00000001 = 2130706433 (主机字节序)
然后把上面的四部分二进制数从右往左按部分重新排列,那就变为:
第三步 00000001 00000000 00000000 01111111 = 16777343 (网络字节序)

internet协议地址结构体中,in_addr.sin_addr必须是负值IP地址的网络字节序
htonl是上述第二步到第三步的转换
inet_addr是上述第一步到第三步的转换

addrSrv.sin_addr.S_un.S_addr=htonl(2130706433);这句还可以写为:
addrSrv.sin_addr.S_un.S_addr=inet_addr(“127.0.0.1”); 结果是完全一样的。

例二:端口的主机字节序与网络字节序的转换。以6000端口为例
1
端口号其实就已经是主机字节序了,首先要把端口号写为16位的二进制数,分前8位和后8位。
第一步 00010111 01110000 = 6000 (主机字节序)
然后把主机字节序的前八位与后八位调换位置组成新的16位二进制数,这新的16位二进制数就是网络字节序的二进制表示了。
第二步 01110000 00010111 = 28695 (网络字节序)

如果你知道6000端口的网络字节序是28695的话。
addrSrv.sin_port=htons(6000);还可以写为
addrSrv.sin_port=28695;结果是一样的,

网络字节序与主机字节序互转 (例1一中的第二步到第三步;例2的第一步到第二步)
1
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

点分十进制串转换成网络字节序(例一中的第一步到第三步)

int inet_pton(int family,const char * strptr,void * addrptr);
功能同inet_addr
将点分十进制串转换成网络字节序二进制值,此函数对IPv4地址和IPv6地址都能处理。
const char * inet_ntop(int family,const void *addrptr,char * strptr,size_t len);
将网络字节序二进制值转换成点分十进制串。
————————————————
版权声明:本文为CSDN博主「wx+15954853403」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38534337/article/details/84316251

  1. inet_pton()源码

    int inet_pton(int family, const char *strptr, void *addrptr)
    {
    if (family == AF_INET) {
    	    struct in_addr  in_val;
    		if (inet_aton(strptr, &in_val)) {
    		    memcpy(addrptr, &in_val, sizeof(in_val));
    		    return (1);
    		}
    	}
    	errno = EAFNOSUPPOPT;
    	return (-1);
    }
    
    1. inet_ntop()源码
      cpp const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len) { const u_char *p = (const u_char*)addrptr; if (family == AF_INET) { char temp[INET_ADDRSTRLEN]; snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); if (strlen(temp) >= len) { errno = ENOSPC; rturn (NULL); } strcpy(strptr, temp); return (strptr); } errno = EAFNOSUPPOPT; return (NULL); }
  2. inet_addr()
    转换网络主机地址(点分十进制)为网络字节序二进制值,如果参数 char *cp 无效则返回-1(INADDR_NONE),但这个函数有个缺点:在处理地址为255.255.255.255时也返回-1,虽然它是一个有效地址,但inet_addr()无法处理这个地址。

in_addr_t inet_addr(const char *cp);
1

  1. inet_ntoa()------->静态函数
    inet_ntoa()函数转换网络字节序地址->标准的点分十进制地址。该函数返回值指向保存点分十进制的字符串地址的指针,该字符串的空间为静态分配 的,所以在第二次调用这个函数时,意味着上一次调用并保存的结果将会被覆盖(重写)。so creazy!!!

char *inet_ntoa(struct in_addr in);
1
好了那就来证实一下,inet_ntoa()的静态返回值吧!!

char *add1,add2;
src.sin_addr.s_addr  =  inet_addr("192.168.1.123");
add1 =inet_ntoa(src.sin_addr);                  
src.sin_addr.s_addr = inet_addr("192.168.1.124");
add2 = inet_ntoa(src.sin_addr);
 
printf("a1:%s\n",add1);
printf("a2:%s\n",add2);

最终的printf结果是:
a1:192.168.1.124
a2:192.168.1.124

  • 这样来看的话,我认为如果有需要最好是用inet_pton()、inet_ntop()代替inet_ntoa()、inet_addr().

    inet_pton(AF_INET, cp, &src.sin_addr);
    代替
    src.sin_addr.s_addr = inet_addr(cp);

char str[INET_ADDRATRLEN];
ptr = inet_ntop(AF_INET, &src.sin_addr, str, sizeof(str));
代替
ptr = inet_ntoa(src.sin_addr);
————————————————
版权声明:本文为CSDN博主「lockin.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43858843/article/details/98960756

`