1、字节序:
多字节数据在内存中数据内字节存储顺序,或者是在网络中数据内字节的传输顺序。
分为:主机字节序和网络字节序(网络字节序定死为大端格式)
大端:高字节低地址
2、主机字节序,网络字节序转换函数
htonl:host to network long (32bit),将32bit的数据从 主机字节序
转换为 网络字节序
ntohl:network to host long (32bit),将32bit的数据从 网络字节序
转换为 主机字节序
htons:host to network short (16bit),将16bit的数据从 主机字节序
转换为 网络字节序
ntohs:network to host short (16bit),将16bit的数据从网络字节序
转换为 主机字节序
// 32位数据类型从host(主机字节序)转换为network(网络字节序,大端)
uint32_t htonl(uint32_t hostlong);
// 32位数据从network(网络字节序)转换为host(主机字节序)
uint32_t ntohl(uint32_t netlong);
// 16位数据类型从host(主机字节序)转换为network(网络字节序,大端)
uint16_t htons(uint16_t hostshort);
// 16位数据从network(网络字节序)转换为host(主机字节序)
uint16_t ntohs(uint16_t netshort);
字节序转换函数转换的是ip地址(htonl, ntohl),端口号(htons, ntohs)。
而不是传输的内容。因为传输的内容已经被序列化为一个个字符了,不存在多字节数据内的字节序。
jl@jl-virtual-machine:~/test$ gcc test.c
jl@jl-virtual-machine:~/test$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
int a = 0x12345678;
int b;
b = htonl(a);
printf("a=%#x\n", a);
printf("htonl(a)=%#x\n", htonl(a));
return 0;
}
jl@jl-virtual-machine:~/test$ ./a.out
a=0x12345678
htonl(a)=0x78563412
jl@jl-virtual-machine:~/test$
3、struct sockaddr,struct sockaddr_in 和 struct sockaddr_un
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET,表示使用 ipv4。
sa_data是14字节协议地址。
此数据结构用做bind、connect、accept、recvfrom、sendto等函数的参数,指明地址信息。
但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构,struct sockaddr_in。
struct sockaddr 是通用的套接字地址,而 struct sockaddr_in 则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
// 协议族,在网络socket编程中一般是AF_INET,表示 ipv4
short int sin_family; /* Address family */
// 端口号(注意使用网络字节顺序)
unsigned short int sin_port; /* Port number */
// 此结构体用来存储ip地址,详见下
struct in_addr sin_addr; /* Internet address */
// 此数组用来让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
// 此结构体用来保存ip地址
struct in_addr {
// 32bit, 等价于 in_addr_t
unsigned long s_addr;
};
具体用法示例,以及 sockaddr_un 相关见:struct sockaddr与struct sockaddr_in ,struct sockaddr_un的区别和联系
4、字符串表示的点分十进制ip地址 与 struct in_addr
两个函数:
int inet_aton(const char *cp, struct in_addr *inp);
用于将cp指向的字符串,转换成为 in_addr 对应的ip地址
如:将字符串“192.168.1.66”转换成in_addr类型的ip地址
返回1表示成功,0表示失败
char *inet_ntoa(struct in_addr in);
将 in_addr 类型的ip地址转换成诸如 “192.168.1.66”格式的ip地址
The string is returned in a statically allocated buffer, which subsequent calls will overwrite.
jl@jl-virtual-machine:~/test$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
struct in_addr a;
char b[16] = "192.168.1.6";
char *c;
char d[16];
int e;
e = inet_aton(b, &a);
printf("ret val = %d\n", e);
printf("%s\n", inet_ntoa(a));
c = inet_ntoa(a);
printf("c=%s\n", c);
return 0;
}
jl@jl-virtual-machine:~/test$ gcc test.c
jl@jl-virtual-machine:~/test$ ./a.out
ret val = 1
192.168.1.6
c=192.168.1.6
返回的是一个静态分配的buffer,只能用指针接收
jl@jl-virtual-machine:~/test$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
struct in_addr a;
char b[16] = "192.168.1.6";
char *c;
char d[16];
int e;
e = inet_aton(b, &a);
printf("ret val = %d\n", e);
printf("%s\n", inet_ntoa(a));
d = inet_ntoa(a);
printf("c=%s\n", d);
return 0;
}
jl@jl-virtual-machine:~/test$ gcc test.c
test.c: In function ‘main’:
test.c:18:7: error: assignment to expression with array type
d = inet_ntoa(a);
^
jl@jl-virtual-machine:~/test$
5、hostname 与 in_addr 相互转换
先介绍:域名&网络地址结构体
struct hostent
{
char *h_name; //主机名,即官方域名
char **h_aliases; //主机所有别名构成的字符串数组,同一IP可绑定多个域名
int h_addrtype; //主机IP地址的类型,例如IPV4(AF_INET)还是IPV6
int h_length; //主机IP地址长度,IPV4地址为4,IPV6地址则为16
char **h_addr_list; /* 主机的ip地址,以网络字节序存储。若要打印出这个IP,需要调用inet_ntoa()。*/
//上面这个二级指针相当于一个字符串数组,每个元素(就是char *)对应一个 struct in_addr*
};
#define h_addr h_addr_list[0];
域名转ip地址函数
struct hostent *gethostbyname(const char *name)
例程:获取域名www.baidu.com的ip地址
jl@jl-virtual-machine:~/test$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char** argv)
{
int i;
struct in_addr my_addr;
struct hostent *myhost;
myhost = gethostbyname("www.baidu.com");
printf("www.baidu.com ip:\n");
for(i=0; myhost->h_addr_list[i]; i++)
{
printf("%s\n", inet_ntoa(*(struct in_addr *)myhost->h_addr_list[i]));
}
return 0;
}
jl@jl-virtual-machine:~/test$ gcc test.c
jl@jl-virtual-machine:~/test$ ./a.out
www.baidu.com ip:
36.152.44.95
36.152.44.96
jl@jl-virtual-machine:~/test$
jl@jl-virtual-machine:~/test$ ping 36.152.44.95
PING 36.152.44.95 (36.152.44.95) 56(84) bytes of data.
64 bytes from 36.152.44.95: icmp_seq=1 ttl=58 time=8.30 ms
64 bytes from 36.152.44.95: icmp_seq=2 ttl=58 time=8.90 ms
^C
--- 36.152.44.95 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 8.308/8.604/8.900/0.296 ms
jl@jl-virtual-machine:~/test$
jl@jl-virtual-machine:~/test$
jl@jl-virtual-machine:~/test$ ping 36.152.44.96
PING 36.152.44.96 (36.152.44.96) 56(84) bytes of data.
64 bytes from 36.152.44.96: icmp_seq=1 ttl=58 time=14.7 ms
64 bytes from 36.152.44.96: icmp_seq=2 ttl=58 time=13.2 ms
64 bytes from 36.152.44.96: icmp_seq=3 ttl=58 time=18.9 ms
64 bytes from 36.152.44.96: icmp_seq=4 ttl=58 time=18.5 ms
^C
--- 36.152.44.96 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 13.223/16.369/18.954/2.465 ms
jl@jl-virtual-machine:~/test$
另一个示例见:域名和网络地址结构体—struct hostent