美文网首页
网络编程中的数据结构与API

网络编程中的数据结构与API

作者: imec | 来源:发表于2017-04-19 20:33 被阅读0次

    在网络编程中,网络层数据结构存储了网络传输的地址族,目的ip地址,目的端口号等重要信息,socket API为程序员提供了通用且方便的接口去实现发送、接收等网络包处理操作。

    数据结构

    通用地址结构体

    struct sockaddr

    /* Structure describing a generic socket address.  */
    struct sockaddr
    {
       __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
       char sa_data[14];           /* Address data.  */
    };
    

    sockaddr 结构体共16个字节,是socket编程中标准的地址结构体。几乎所有的socket API均使用sockaddr作为其地址信息存储结构。但由于定义它的时候还处于IPv4的年代,并没有预料到会有IPv6的诞生,sockaddr大小只有16字节,是无法存储IPv6 128位的IP地址的,因此在socket扩展中加入了第二种通用地址结构。

    struct sockaddr_storage

    /* Structure large enough to hold any socket address (with the historical
       exception of AF_UNIX).  */
    #define __ss_aligntype  unsigned long int
    #define _SS_PADSIZE \
       (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
    
    struct sockaddr_storage
    {
       __SOCKADDR_COMMON (ss_); /* Address family, etc.  */
       char __ss_padding[_SS_PADSIZE];
       __ss_aligntype __ss_align;   /* Force desired alignment.  */
    };
    

    相比sockaddrsockaddr_storage结构体的内存空间扩展到了128字节(__SOCKADDR_COMMON_SS_SIZE 等通用结构体成员的宏定义在 <bits/sockaddr.h> 内),足以存储IPv6地址。因此,在IPv6网络编程时,绝大多数情况下使用 sockaddr_storage 存储网络地址信息,同时为了与老代码保持一致性,在调用socket API时还需使用强制类型转换转换为 sockaddr

    以上两种通用地址结构的原始定义位于 <bits/socket.h> 内,在使用时,包含头文件 <sys/socket.h> 即可。另外在Ubuntu 64位系统中,系统头文件并不是位于 /usr/include/sys,而是 /usr/include/x86_64-linux-gnu/sys。至于编译器怎么将 #include <sys/socket.h> 定向到 /usr/include/x86_64-linux-gnu/sys/socket.h 的,目前还没弄明白=,=。

    struct addrinfo

    struct addrinfo{  
        int ai_flags;                         /* AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST */  
        int ai_family;                        /* AF_INET,AF_INET6 */  
        int ai_socktype;                   /* SOCK_STREAM,SOCK_DGRAM */  
        int ai_protocol;                   /* IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 */  
        size_t ai_addrlen;               /* Length */  
        char *ai_cannoname;         /* */  
        struct sockaddr *ai_addr;   /* struct sockaddr */  
        struct addrinfo *ai_next;      /* pNext */  
    }  
    

    addrinfo 是用于 getaddrinfo 函数中存储地址信息的。在IPv6编程中,getaddrinfo 取代了原有的 gethostbyaddr, gethostbyname, getservbyname, getservbyaddr等函数,通过主机名或者ip地址获取相应的地址信息。

    IPv4

    头文件

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h> /* superset of previous */
    

    netinet 还是位于 /usr/include 内。

    地址结构

    struct sockaddr_in {           /* 16 bytes */
       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 */
    };
    

    AF_INET 值为2。

    IPv6

    头文件

    #include <sys/socket.h>
    #include <netinet/in.h>
    

    地址结构

    struct sockaddr_in6 {              /* 28 bytes */
        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 */
    };
    

    AF_INET6 值为10。

    socket API

    头文件

    #include <sys/socket.h>
    

    基本函数

    • socket
    • bind
    • listen
    • connect
    • read/write
    • send/recv
    • sendto/recvfrom
    • close
    • getsockopt
    • setsockopt

    IPv4 only函数

    • gethostbyaddr
    • gethostbyname
    • inet_aton
    • inet_ntoa

    IPv4/v6 通用函数

    • getpeername
    • getsockname
    • getaddrinfo
    • inet_ntop
    • inet_pton
    • htons/htonl
    • ntohs/ntohl

    IPv4 to IPv4/v6

    通常我们会遇到一些历史遗留代码不支持IPv6通信,因此我们需要对原代码进行一定的改造,让其支持IPv6。

    Tips:

    • getaddrinfo() 来获取 struct sockaddr 的信息,而不是手动填写这个数据结构。这样就可以忽略 IP 的版本
    • 找出全部与 IP 版本相关的代码,试着用一个有用的函数将它们封装起来
    • addrinfoai_family 对地址族进行判断,用来对那些跟 IP 版本相关的代码进行处理
    • 通配地址 INADDR_ANYin6addr_any 区别: 前者是一个宏定义,而后者是一个结构体
    #define INADDR_ANY      ((in_addr_t) 0x00000000) 
    extern const struct in6_addr in6addr_any;   /* :: */
    
    • 在声明 struct in6_addr 时,IN6ADDR_ANY_INIT 的值可以做为初始值
    struct in6_addr ia6 = IN6ADDR_ANY_INIT;
    
    • 使用 inet_pton() 替换 inet_aton()inet_addr()
    • 使用 inet_ntop() 替换 inet_ntoa()
    • 使用 getaddrinfo() 取代 gethostbyname()
    • 使用 getnameinfo() 取代 gethostbyaddr()
    • 不要用 INADDR_BROADCAST 了,多使用 IPv6 multicast 来替换(这条还没使用过☺)

    参考文献

    相关文章

      网友评论

          本文标题:网络编程中的数据结构与API

          本文链接:https://www.haomeiwen.com/subject/dywhzttx.html