首页 > 代码库 > linux: 初试网络编程

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]$ ./testip : 180.97.33.107net: 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]$ ./test1net: b461216bip : 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.comhost name: www.a.shifen.comalias : www.baidu.comip type : 2addr len : 4ip : 180.97.33.107ip : 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,都是直接以位运算形式实现的,因此其关注的是数据在内存中实际的二进制存储形式。

linux: 初试网络编程