linux: 初试网络编程
socket信息数据结构
#include <netinet/in.h> struct sockaddr { unsigned short sa_family; /*地址族*/ char sa_data[14]; /*14字节的协议地址,包含该socket的IP地址和端口号。*/ }; struct sockaddr_in { short int sa_family; /*地址族*/ unsigned short int sin_port; /*端口号*/ struct in_addr sin_addr; /*IP地址*/ unsigned char sin_zero[8]; /*填充0 以保持与struct sockaddr同样大小*/ }; struct in_addr { unsigned long int s_addr; /* 32位IPv4地址,网络字节序 */ }; #include <netinet/in.h> tips
sa_family: AF_INET -> IPv4协议 AF_INET6 -> IPv6协议
注意
结构体struct in_addr中存放的s_addr,是无符号整型数。实际上32位IPv4地址为点分十进制,每个字节的范围均为0-255,只要高字节大于等于128,那么这个整型数必然为负数,只不过我们这边仅仅关心ip每一位的存储情况,因此此处可以使用无符号数进行存储。
函数原型1
SYNOPSIS #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp);/* 注意,参数inp为传出参数 */ char *inet_ntoa(struct in_addr in);
实际上,我们在上篇文章中实现的三个函数是有系统函数可以直接调用的。我们的my_atoh,my_hton合并为系统函数inet_aton,而my_ntoa即为系统函数inet_ntoa。
举例1
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { char ip_buf[] = "180.97.33.107"; struct in_addr my_addr; inet_aton(ip_buf,&my_addr); printf("ip : %s \n", ip_buf); printf("net: %x \n", my_addr.s_addr); return 0; }
运行结果
[purple@localhost 0827]$ gcc -o test test.c -Wall [purple@localhost 0827]$ ./test ip : 180.97.33.107 net: 6b2161b4
照理,网络字节序是大端存储,应该返回0xb461216b。实际上调用系统函数inet_aton后,就直接在变量my_addr.s_addr的实际内存空间中以二进制形式写入了0xb461216b(其实用位运算,就可以直接操作二进制位,上篇博文有具体实现)。之所以运行结果是0x6b2161b4,是因为我们的主机是小端存储,用printf显示结果是先取低字节。
举例2
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { struct in_addr my_addr; my_addr.s_addr = 0xb461216b; printf("net: %x \n", my_addr.s_addr); printf("ip : %s \n", inet_ntoa(my_addr)); return 0; }
运行结果
[purple@localhost 0827]$ gcc -o test1 test1.c -Wall [purple@localhost 0827]$ ./test1 net: b461216b ip : 107.33.97.180
照理,ip应该输出的是180.97.33.107。其实道理很简单,我们的主机是小端模式存储,而网络字节序是大端模式,当我们把0xb461216b赋值给my_addr.s_addr 时,实际上在内存中存储形式是0x6b2161b4,而inet_ntoa的具体实现时通过位运算直接操纵二进制位的,因此结果就自然输出107.33.97.180。
函数原型2
SYNOPSIS #include <netdb.h> struct hostent *gethostbyname(const char *name); The hostent structure is defined in <netdb.h> as follows: struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ } #define h_addr h_addr_list[0] /* for backward compatibility */ The members of the hostent structure are: h_name The official name of the host. h_aliases An array of alternative names for the host, terminated by a NULL pointer. h_addrtype The type of address; always AF_INET or AF_INET6 at present. h_length The length of the address in bytes. h_addr_list An array of pointers to network addresses for the host (in net- work byte order), terminated by a NULL pointer. h_addr The first address in h_addr_list for backward compatibility.
代码
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> int main(int argc, char* argv[])// exe hostname { struct hostent* p ; p = gethostbyname(argv[1]) ; /* struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; } #define h_addr h_addr_list[0] */ printf("host name: %s \n", p ->h_name); int index ; char** pp = p -> h_aliases ; for(index = 0 ; pp[index] != NULL; index ++ ) { printf("alias : %s \n", pp[index]); } printf("ip type : %d\n", p ->h_addrtype); printf("addr len : %d \n", p ->h_length); pp = p ->h_addr_list ; for(index = 0; pp[index] != NULL ; index ++) { /* 由于h_addr_list是一个字符串指针数组,数组中存放的指针指向一个网络字节序 但是系统函数inet_ntoa需要传入的参数是一个结构体,因此需要进行转换。 pp[index]是一个char*类型的指针,先将其转换为struct in_addr*类型的指针, 接着去引用,即得到结构体。 */ printf("ip : %s \n", inet_ntoa( *(struct in_addr *)pp[index] ) ); } return 0 ; }
运行结果
[purple@localhost 0827]$ gcc -o myhost my_host.c -Wall [purple@localhost 0827]$ ./myhost www.baidu.com host name: www.a.shifen.com alias : www.baidu.com ip type : 2 addr len : 4 ip : 180.97.33.107 ip : 180.97.33.108
干货
某年腾讯面试题:
#include <stdio.h> #include <stdlib.h> int main() { int a = 0x61;//97 内存中的情况是:小端 61 00 00 00 如果是大端机 00 00 00 61 两种情况 printf("%x\n",(char*)(&a)[0]); 把&a转化为char*是为了只指向一个字节 int*指向的是4个字节 }
结果输出61,说明是小端机,先存低字节。
总结
切记系统函数无论inet_aton还是inet_ntoa,都是直接以位运算形式实现的,因此其关注的是数据在内存中实际的二进制存储形式。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。