• LINUX 下 ipv6 socket 编程


    大家都知道,随着互联网上主机数量的增多,现有的32位IP地址已经不够用了,所以推出了下一代IP地址IPv6,写网络程序的要稍微改变一下现有的网络程序适应IPv6网络是相当容易的事。
    对于我们来说就是IP地址变化了,所以程序里在用到IP地址的地方做相应的改变就可以了。

    记住:主要是改变程序里设置IP地址和端口等部分的代码。

    服务器端源代码如下:
    /***********************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #define MAXBUF 1024
    /************关于本文档********************************************
    *filename: ipv6-server.c
    *purpose: 演示最基本的IPv6网络编程步骤,开启服务接收客户端连接并和客户端通信,互相收发消息
    *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
    Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
    *date time:2007-01-29 13:06
    *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
    * 但请遵循GPL
    *Thanks to:Google
    *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
    * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!

    inet_aton() 转换网络主机地址cp为二进制数值,并存储在struct in_addr结构中,即第二个参数*inp,函数返回非0表示cp主机有地有效,返回0表示主机地址无效。
    inet_addr 函数转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp无效,函数返回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回 -1,255.255.255.255是一个有效的地址,不过inet_addr无法处理;
    inet_ntoa 函数转换网络字节排序的地址为标准的ASCII以点分开的地址,,该函数返回指向点分开的字符串地址的指针,该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖),所以如果需要保存该串最后复制出来自己管理!
    *********************************************************************/
    int main(int argc, char **argv)
    {
        int sockfd, new_fd;
        socklen_t len;
        /* struct sockaddr_in my_addr, their_addr; */ // IPv4
        struct sockaddr_in6 my_addr, their_addr; // IPv6
        unsigned int myport, lisnum;  //myport:端口号,lisnum最大连接数
        char buf[MAXBUF + 1];
        if (argv[1])
            myport = atoi(argv[1]);//atoi /把字符串转化为整形,
        else
            myport = 7838;
        if (argv[2])
            lisnum = atoi(argv[2]);
        else
            lisnum = 2;
        /* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4
        if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6
            perror("socket");
            exit(1);
        } else
            printf("socket created ");
        bzero(&my_addr, sizeof(my_addr));
        /* my_addr.sin_family = PF_INET; */ // IPv4
        my_addr.sin6_family = PF_INET6;    // IPv6
        /* my_addr.sin_port = htons(myport); */ // IPv4
        my_addr.sin6_port = htons(myport);   // IPv6  htons :将主机的无符号短整形数转 换成网络字节顺序。
        if (argv[3])
            /* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4
            inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr);  // IPv6
    /*#include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    int inet_pton(int af, const char *src, void *dst);

    这个函数转换字符串到网络地址,第一个参数af是地址族,转换后存在dst中
    inet_pton 是inet_addr的扩展,支持的多地址族有下列:

    AF_INET
           src为指向字符型的地址,即ASCII的地址的首地址(ddd.ddd.ddd.ddd格式的),函数将该地址
           转换为in_addr的结构体,并复制在*dst中

    AF_INET6
           src为指向IPV6的地址,,函数将该地址
           转换为in6_addr的结构体,并复制在*dst中
    如果函数出错将返回一个负值,并将errno设置为EAFNOSUPPORT,如果参数af指定的地址族和src格式不对,函数将返回0。

    函数inet_ntop进行相反的转换原型如下
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
    这个函数转换网络二进制结构到ASCII类型的地址,参数的作用和上面相同,只是多了一个参数socklen_t cnt,他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC
    */
        else
            /* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4
            my_addr.sin6_addr = in6addr_any;            // IPv6
        /* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4
        if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6))  // IPv6
            == -1) {
            perror("bind");
            exit(1);
        } else
            printf("binded ");
        if (listen(sockfd, lisnum) == -1) {
            perror("listen");
            exit(1);
        } else
            printf("begin listen ");
        while (1) {
            len = sizeof(struct sockaddr);
            if ((new_fd =
                 accept(sockfd, (struct sockaddr *) &their_addr,
                        &len)) == -1) {
                perror("accept");
                exit(errno);
            } else
                printf("server: got connection from %s, port %d, socket %d ",
                       /* inet_ntoa(their_addr.sin_addr), */ // Ipv4
                   inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6
                       /* ntohs(their_addr.sin_port), new_fd); */ // IPv4
                       their_addr.sin6_port, new_fd); // IPv6

            /* 开始处理每个新连接上的数据收发 */
            bzero(buf, MAXBUF + 1);
            strcpy(buf,
                   "这是在连接建立成功后向客户端发送的第一个消息 只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息 ");
            /* 发消息给客户端 */
            len = send(new_fd, buf, strlen(buf), 0);
            if (len < 0) {
                printf
                    ("消息'%s'发送失败!错误代码是%d,错误信息是'%s' ",
                     buf, errno, strerror(errno));
            } else
                printf("消息'%s'发送成功,共发送了%d个字节! ",
                       buf, len);

            bzero(buf, MAXBUF + 1);
            /* 接收客户端的消息 */
            len = recv(new_fd, buf, MAXBUF, 0);
            if (len > 0)
                printf("接收消息成功:'%s',共%d个字节的数据 ",
                       buf, len);
            else
                printf
                    ("消息接收失败!错误代码是%d,错误信息是'%s' ",
                     errno, strerror(errno));
            /* 处理每个新连接上的数据收发结束 */
        }

        close(sockfd);
        return 0;
    }

    每行程序后面的 “//IPv4” 表示这行代码是在IPv4网络里用的
    而“//IPv6” 表示这行代码是在IPv6网络里用的,比较一下,会很容易看到差别的。

    客户端源代码如下:
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <resolv.h>                     //?
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #define MAXBUF 1024
    /************关于本文档********************************************
    *filename: ipv6-client.c
    *purpose: 演示最基本的IPv6网络编程步骤,这是个客户端程序,与服务器互相收发消息
    *wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
    Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
    *date time:2007-01-29 12:56
    *Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
    * 但请遵循GPL
    *Thanks to:Google
    *Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
    * 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
    *********************************************************************/
    int main(int argc, char **argv)
    {
        int sockfd, len;
        /* struct sockaddr_in dest; */ // IPv4
        struct sockaddr_in6 dest;      // IPv6
        char buffer[MAXBUF + 1];
        if (argc != 3) {
            printf
                ("参数格式错误!正确用法如下: %s IP地址 端口 比如: %s 127.0.0.1 80 此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
                 argv[0], argv[0]);
            exit(0);
        }
        /* 创建一个 socket 用于 tcp 通信 */
        /* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4
        if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6
            perror("Socket");
            exit(errno);

      }
        printf("socket created ");

        /* 初始化服务器端(对方)的地址和端口信息 */
        bzero(&dest, sizeof(dest));
        /* dest.sin_family = AF_INET; */  // IPv4
        dest.sin6_family = AF_INET6;     // IPv6
        /* dest.sin_port = htons(atoi(argv[2])); */ // IPv4
        dest.sin6_port = htons(atoi(argv[2]));     // IPv6
        /* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4
        if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6
            perror(argv[1]);
            exit(errno);
        }
        printf("address created ");

        /* 连接服务器 */
        if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
            perror("Connect ");
            exit(errno);
        }
        printf("server connected ");

        /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
        bzero(buffer, MAXBUF + 1);
        /* 接收服务器来的消息 */
        len = recv(sockfd, buffer, MAXBUF, 0);
        if (len > 0)
            printf("接收消息成功:'%s',共%d个字节的数据 ",
                   buffer, len);
        else
            printf
                ("消息接收失败!错误代码是%d,错误信息是'%s' ",
                 errno, strerror(errno));

        bzero(buffer, MAXBUF + 1);
        strcpy(buffer, "这是客户端发给服务器端的消息 ");
        /* 发消息给服务器 */
        len = send(sockfd, buffer, strlen(buffer), 0);
        if (len < 0)
            printf
                ("消息'%s'发送失败!错误代码是%d,错误信息是'%s' ",
                 buffer, errno, strerror(errno));
        else
            printf("消息'%s'发送成功,共发送了%d个字节! ",
                   buffer, len);

        /* 关闭连接 */
        close(sockfd);
        return 0;
    }


    编译程序用下列命令:
    引用:
    gcc -Wall ipv6-server.c -o ipv6server
    gcc -Wall ipv6-client.c -o ipv6client

  • 相关阅读:
    [BZOJ2071] [POI2004]JAS
    [BZOJ1852] [MexicoOI06]最长不下降序列(dp+贪心转移)
    用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)(转)
    Amazon云计算的一些实用应用(转)
    使用netbeans6.7.1开发webservice 服务端 和 客户端(转)
    深入探索SOAP1.1使用SAAJ1.2.1(转)
    推荐:PoolParty!一个管理EC2集群的Ruby Gem开源工具(转)
    max 加载 菜单项
    2011 新相
    重要的视图类型解释。
  • 原文地址:https://www.cnblogs.com/dancheblog/p/4126258.html
Copyright © 2020-2023  润新知