Chinaunix首页 | 论坛 | 博客
  • 博客访问: 616653
  • 博文数量: 127
  • 博客积分: 6136
  • 博客等级: 准将
  • 技术积分: 1461
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 00:32

分类: C/C++

2010-11-28 15:33:34

关于网络编程,我想先从套接字的地址结构说起。
1.地址结构
IPV4套接字地址结构:

<netinet/in.h>
struct in_addr
{
    in_addr_t        s_addr;
};
struct sockaddr_in
{
    uint8_t         sin_len;//结构体长度
    sa_family_t    sin_family;//AF_INET
    in_port_t        sin_port;
    struct in_addr    sin_addr;
    char         sin_zero[8];
};

通用套接字地址结构:

<sys/socket.h>
struct sockaddr
{
    uint8_t        sa_len;
    sa_family_t    sa_family;
    char            sa_data[14];
}


IPV6套接字地址结构:

<netinet/in.h>
struct in6_addr
{
    uint8_t        s6_addr[16];        //128bit ipv6 address
};
struct sockaddr_in6
{
    uint8_t         sin6_len;//结构体长度
    sa_family_t    sin6_family;//AF_INET6
    in_port_t        sin6_port;
    uint32_t        sin6_flowinfo;    //undefined
    struct in6_addr    sin6_addr;
    uint32_t        sin6_scope_id;    //set of interfaces for a scope
}

2.字节序列
学过计算机原理的人对大端方案和小端方案并不陌生,比如系统在存储0x1234时,若存在硬盘的是0x12 0x34序列,那么就是大端方案;如果是0x34 0x12,那就是小端方案,这里写一个程序可以测试下系统所使用的字节序列。

#include <stdio.h>
int main()
{
    union
    {
        short s;
        char a[sizeof(short)];
    }un;
    un.s = 0x0102;
    if(un.a[0] == 0x01 && un.a[1] == 0x02)
        printf("big-endian\n");
    else if(un.a[0] == 0x02 && un.a[1] == 0x01)
        printf("little-endian\n");
    return 0;
}

不同的系统会使用不同的字节序列方案,但是,网际协议使用的是大端字节序列来传送这些多字节整数。因此,我们就需要在主机字节序列和网络字节序列间转换的函数。
3.字节序列转换函数

#include <netinet/in.h>
uint16_t    htons(uint16_t host16bitvalue);
uint32_t    htonl(uint32_t host32bitvalue);//均返回网络字节序的值

uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);//均返回主机字节序的值

这些函数中,h表示host,n表示net,l表示long,s表示short
4.地址转换函数
#include
int inet_aton(const char *strptr, struct in_addr *addrptr);
//将strptr所指向的点分十进制串转换成一个32位的网络字节序的二进制值,若字符串有效返回1,否则返回0

in_addr_t inet_addr(const char *strptr);
//该函数与inet_aton做同样的事,但是该函数存在一个问题,就是255.255.255.255处理不了。因为该函数出错是返回INADDR_NONE(32位均为1),而255.255.255.255的二进制也是全为1。该函数已经被废弃,应使用inet_aton代替。

char *inet_ntoa(struct in_addr inaddr);
//返回一个点分十进制的指针,该函数需要注意的是,该函数的返回值所指向的字符串是驻留在静态内存中的,这意味着该函数不可重入。另外,该函数的参数不是指针,这是非常少见的。

以上的函数只能针对IPV4,下面的2个函数对IPV4和IPV6都适用。

int inet_pton(int af, const char *src, void *dst);
const char * inet_ntop(int af, const void *src, char *dst, size_t size);

接下来我们看一下这两个函数在freebsd上的实现:
int inet_pton(int af, const char *src, void *dst);

static int inet_pton4(const char *src, u_char *dst);
static int inet_pton6(const char *src, u_char *dst);

/* int
 * inet_pton(af, src, dst)
 * convert from presentation format (which usually means ASCII printable)
 * to network format (which is usually some kind of binary format).
 * return:
 * 1 if the address was valid for the specified address family
 * 0 if the address wasn't valid (`dst' is untouched in this case)
 * -1 if some other error occurred (`dst' is untouched in this case, too)
 * author:
 * Paul Vixie, 1996.
 */

int
inet_pton(int af, const char *src, void *dst)
{
        switch (af) {
        case AF_INET:
                return (inet_pton4(src, dst));
        case AF_INET6:
                return (inet_pton6(src, dst));
        default:
                errno = EAFNOSUPPORT;
                return (-1);
        }
        /* NOTREACHED */
}

/* int
 * inet_pton4(src, dst)
 * like inet_aton() but without all the hexadecimal and shorthand.
 * return:
 * 1 if `src' is a valid dotted quad, else 0.
 * notice:
 * does not touch `dst' unless it's returning 1.
 * author:
 * Paul Vixie, 1996.
 */

static int
inet_pton4(const char *src, u_char *dst)
{
        static const char digits[] = "0123456789";
        int saw_digit, octets, ch;
        u_char tmp[INADDRSZ], *tp;
        saw_digit = 0;
        octets = 0;
        *(tp = tmp) = 0;
        while ((ch = *src++) != '\0') {
                const char *pch;
                if ((pch = strchr(digits, ch)) != NULL) {
                        u_int new = *tp * 10 + (pch - digits);//**可以通过指针直接获取某一项的值
                        if (new > 255)
                                return (0);
                        if (! saw_digit) {
                                if (++octets > 4)
                                        return (0);
                                saw_digit = 1;
                        }
                        *tp = new;
                } else if (ch == '.' && saw_digit) {
                        if (octets == 4)
                                return (0);
                        *++tp = 0;
                        saw_digit = 0;
                } else
                        return (0);
        }
        if (octets < 4)
                return (0);
        memcpy(dst, tmp, INADDRSZ);
        return (1);
}

/* int
 * inet_pton6(src, dst)
 * convert presentation level address to network order binary form.
 * return:
 * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
 * notice:
 * (1) does not touch `dst' unless it's returning 1.
 * (2) :: in a full address is silently ignored.
 * credit:
 * inspired by Mark Andrews.
 * author:
 * Paul Vixie, 1996.
 */

static int
inet_pton6(const char *src, u_char *dst)
{
        static const char xdigits_l[] = "0123456789abcdef",
                          xdigits_u[] = "0123456789ABCDEF";
        u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
        const char *xdigits, *curtok;
        int ch, saw_xdigit, count_xdigit;
        u_int val;
        memset((tp = tmp), '\0', IN6ADDRSZ);
        endp = tp + IN6ADDRSZ;
        colonp = NULL;
        /* Leading :: requires some special handling. */
        if (*src == ':')
                if (*++src != ':')
                        return (0);
        curtok = src;
        saw_xdigit = count_xdigit = 0;
        val = 0;
        while ((ch = *src++) != '\0') {
                const char *pch;
                if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
                        pch = strchr((xdigits = xdigits_u), ch);
                if (pch != NULL) {
                        if (count_xdigit >= 4)
                                return (0);
                        val <<= 4;
                        val |= (pch - xdigits);
                        if (val > 0xffff)
                                return (0);
                        saw_xdigit = 1;
                        count_xdigit++;
                        continue;
                }
                if (ch == ':') {
                        curtok = src;
                        if (!saw_xdigit) {
                                if (colonp)
                                        return (0);
                                colonp = tp;
                                continue;
                        } else if (*src == '\0') {
                                return (0);
                        }
                        if (tp + INT16SZ > endp)
                                return (0);
                        *tp++ = (u_char) (val >> 8) & 0xff;
                        *tp++ = (u_char) val & 0xff;
                        saw_xdigit = 0;
                        count_xdigit = 0;
                        val = 0;
                        continue;
                }
                if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
                    inet_pton4(curtok, tp) > 0) {
                        tp += INADDRSZ;
                        saw_xdigit = 0;
                        count_xdigit = 0;
                        break; /* '\0' was seen by inet_pton4(). */
                }
                return (0);
        }
        if (saw_xdigit) {
                if (tp + INT16SZ > endp)
                        return (0);
                *tp++ = (u_char) (val >> 8) & 0xff;
                *tp++ = (u_char) val & 0xff;
        }
        if (colonp != NULL) {
                /*
                 * Since some memmove()'s erroneously fail to handle
                 * overlapping regions, we'll do the shift by hand.
                 */

                const int n = tp - colonp;
                int i;
                for (i = 1; i <= n; i++) {
                        endp[- i] = colonp[n - i];
                        colonp[n - i] = 0;
                }
                tp = endp;
        }
        if (tp != endp)
                return (0);
        memcpy(dst, tmp, IN6ADDRSZ);
        return (1);
}

const char *
inet_ntop(int af, const void *src, char *dst, size_t size)

static const char *inet_ntop4(const u_char *src, char *dst, size_t size);
static const char *inet_ntop6(const u_char *src, char *dst, size_t size);

const char *
inet_ntop(int af, const void *src, char *dst, size_t size)
{
        switch (af) {
        case AF_INET:
                return (inet_ntop4(src, dst, size));
        case AF_INET6:
                return (inet_ntop6(src, dst, size));
        default:
                errno = EAFNOSUPPORT;
                return (NULL);
        }
        /* NOTREACHED */
}

static const char *
inet_ntop4(const u_char *src, char *dst, size_t size)
{
        static const char fmt[] = "%u.%u.%u.%u";
        char tmp[sizeof "255.255.255.255"];
        int l;

        l = snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]);

        if (l <= 0 || (size_t)l >= size) {
                errno = ENOSPC;
                return (NULL);
        }
        strlcpy(dst, tmp, size);
        return (dst);
}

static const char *
inet_ntop6(const u_char *src, char *dst, size_t size)
{
        char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
        char *tp, *ep;
        struct { int base, len; } best, cur;
        u_int words[IN6ADDRSZ / INT16SZ];
        int i;
        int advance;

        memset(words, '\0', sizeof words);

        for (i = 0; i < IN6ADDRSZ; i++)
                words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));

        best.base = -1;
        best.len = 0;
        cur.base = -1;
        cur.len = 0;
        for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
                if (words[i] == 0) {
                        if (cur.base == -1)
                                cur.base = i, cur.len = 1;
                        else
                                cur.len++;
                } else {
                        if (cur.base != -1) {
                                if (best.base == -1 || cur.len > best.len)
                                        best = cur;
                                cur.base = -1;
                        }
                }
        }
        if (cur.base != -1) {
                if (best.base == -1 || cur.len > best.len)
                        best = cur;
        }

        if (best.base != -1 && best.len < 2)
                best.base = -1;

        tp = tmp;
        ep = tmp + sizeof(tmp);
        for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) {
                if (best.base != -1 && i >= best.base &&
                    i < (best.base + best.len)) {
                        if (i == best.base) {
                                if (tp + 1 >= ep)
                                        return (NULL);
                                *tp++ = ':';
                        }
                        continue;
                }
                /* Are we following an initial run of 0x00s or any real hex? */
                if (i != 0) {
                        if (tp + 1 >= ep)
                                return (NULL);
                        *tp++ = ':';
                }
                /* Is this address an encapsulated IPv4? */
                if (i == 6 && best.base == 0 &&
                    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
                        if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
                                return (NULL);
                        tp += strlen(tp);
                        break;
                }
                advance = snprintf(tp, ep - tp, "%x", words[i]);
                if (advance <= 0 || advance >= ep - tp)
                        return (NULL);
                tp += advance;
        }
        /* Was it a trailing run of 0x00's? */
        if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) {
                if (tp + 1 >= ep)
                        return (NULL);
                *tp++ = ':';
        }
        if (tp + 1 >= ep)
                return (NULL);
        *tp++ = '\0';
        /*
         * Check for overflow, copy, and we're done.
         */

        if ((size_t)(tp - tmp) > size) {
                errno = ENOSPC;
                return (NULL);
        }
        strlcpy(dst, tmp, size);
        return (dst);
}

在这两段代码中,我们不仅可以了解到这两个函数的具体实现,而且代码的编写风格真的是一流,另外,在代码中,运用了很多的编程技巧。
阅读(1875) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-11-28 22:14:25

太牛了