首页 > C/C++ > 网络编程 > IPv6下网络编程socket, TCP和UDP例子
2017
02-10

IPv6下网络编程socket, TCP和UDP例子

一、TCP socket ipv6与ipv4的区别

服务器端源代码如下:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <string.h>  
  5. #include <sys/types.h>  
  6. #include <netinet/in.h>  
  7. #include <sys/socket.h>  
  8. #include <sys/wait.h>  
  9. #include <unistd.h>  
  10. #include <arpa/inet.h>  
  11. #define MAXBUF 1024  
  12. int main(int argc, char **argv)  
  13. {  
  14.     int sockfd, new_fd;  
  15.     socklen_t len;  
  16.   
  17.     /* struct sockaddr_in my_addr, their_addr; */ // IPv4  
  18.     struct sockaddr_in6 my_addr, their_addr; // IPv6  
  19.   
  20.     unsigned int myport, lisnum;  
  21.     char buf[MAXBUF + 1];  
  22.   
  23.     if (argv[1])  
  24.         myport = atoi(argv[1]);  
  25.     else  
  26.         myport = 7838;  
  27.   
  28.     if (argv[2])  
  29.         lisnum = atoi(argv[2]);  
  30.     else  
  31.         lisnum = 2;  
  32.   
  33.     /* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4  
  34.     if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6  
  35.         perror("socket");  
  36.         exit(1);  
  37.     } else  
  38.         printf("socket created/n");  
  39.   
  40.     bzero(&my_addr, sizeof(my_addr));  
  41.     /* my_addr.sin_family = PF_INET; */ // IPv4  
  42.     my_addr.sin6_family = PF_INET6;    // IPv6  
  43.     /* my_addr.sin_port = htons(myport); */ // IPv4  
  44.     my_addr.sin6_port = htons(myport);   // IPv6  
  45.     if (argv[3])  
  46.         /* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4  
  47.         inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr);  // IPv6  
  48.     else  
  49.         /* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4  
  50.         my_addr.sin6_addr = in6addr_any;            // IPv6  
  51.   
  52.     /* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4  
  53.     if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6))  // IPv6  
  54.         == -1) {  
  55.         perror("bind");  
  56.         exit(1);  
  57.     } else  
  58.         printf("binded/n");  
  59.   
  60.     if (listen(sockfd, lisnum) == -1) {  
  61.         perror("listen");  
  62.         exit(1);  
  63.     } else  
  64.         printf("begin listen/n");  
  65.   
  66.     while (1) {  
  67.         len = sizeof(struct sockaddr);  
  68.         if ((new_fd =  
  69.              accept(sockfd, (struct sockaddr *) &their_addr,  
  70.                     &len)) == -1) {  
  71.             perror("accept");  
  72.             exit(errno);  
  73.         } else  
  74.             printf("server: got connection from %s, port %d, socket %d/n",  
  75.                    /* inet_ntoa(their_addr.sin_addr), */ // IPv4  
  76.                    inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6  
  77.                    /* ntohs(their_addr.sin_port), new_fd); */ // IPv4  
  78.                    their_addr.sin6_port, new_fd); // IPv6  
  79.   
  80.         /* 开始处理每个新连接上的数据收发 */  
  81.         bzero(buf, MAXBUF + 1);  
  82.         strcpy(buf,  
  83.                "这是在连接建立成功后向客户端发送的第一个消息/n只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息/n");  
  84.         /* 发消息给客户端 */  
  85.         len = send(new_fd, buf, strlen(buf), 0);  
  86.         if (len < 0) {  
  87.             printf  
  88.                 ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",  
  89.                  buf, errno, strerror(errno));  
  90.         } else  
  91.             printf("消息'%s'发送成功,共发送了%d个字节!/n",  
  92.                    buf, len);  
  93.   
  94.         bzero(buf, MAXBUF + 1);  
  95.         /* 接收客户端的消息 */  
  96.         len = recv(new_fd, buf, MAXBUF, 0);  
  97.         if (len > 0)  
  98.             printf("接收消息成功:'%s',共%d个字节的数据/n",  
  99.                    buf, len);  
  100.         else  
  101.             printf  
  102.                 ("消息接收失败!错误代码是%d,错误信息是'%s'/n",  
  103.                  errno, strerror(errno));  
  104.         /* 处理每个新连接上的数据收发结束 */  
  105.     }  
  106.   
  107.     close(sockfd);  
  108.     return 0;  
  109. }  

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

 

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <errno.h>  
  4. #include <sys/socket.h>  
  5. #include <resolv.h>  
  6. #include <stdlib.h>  
  7. #include <netinet/in.h>  
  8. #include <arpa/inet.h>  
  9. #include <unistd.h>  
  10. #define MAXBUF 1024  
  11. int main(int argc, char **argv)  
  12. {  
  13.     int sockfd, len;  
  14.     /* struct sockaddr_in dest; */ // IPv4  
  15.     struct sockaddr_in6 dest;      // IPv6  
  16.     char buffer[MAXBUF + 1];  
  17.   
  18.     if (argc != 3) {  
  19.         printf  
  20.             ("参数格式错误!正确用法如下:/n/t/t%s IP地址 端口/n/t比如:/t%s 127.0.0.1 80/n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",  
  21.              argv[0], argv[0]);  
  22.         exit(0);  
  23.     }  
  24.     /* 创建一个 socket 用于 tcp 通信 */  
  25.     /* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4  
  26.     if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6  
  27.         perror("Socket");  
  28.         exit(errno);  
  29.     }  
  30.     printf("socket created/n");  
  31.   
  32.     /* 初始化服务器端(对方)的地址和端口信息 */  
  33.     bzero(&dest, sizeof(dest));  
  34.     /* dest.sin_family = AF_INET; */  // IPv4  
  35.     dest.sin6_family = AF_INET6;     // IPv6  
  36.     /* dest.sin_port = htons(atoi(argv[2])); */ // IPv4  
  37.     dest.sin6_port = htons(atoi(argv[2]));     // IPv6  
  38.     /* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4  
  39.     if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6  
  40.         perror(argv[1]);  
  41.         exit(errno);  
  42.     }  
  43.     printf("address created/n");  
  44.   
  45.     /* 连接服务器 */  
  46.     if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {  
  47.         perror("Connect ");  
  48.         exit(errno);  
  49.     }  
  50.     printf("server connected/n");  
  51.   
  52.     /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */  
  53.     bzero(buffer, MAXBUF + 1);  
  54.     /* 接收服务器来的消息 */  
  55.     len = recv(sockfd, buffer, MAXBUF, 0);  
  56.     if (len > 0)  
  57.         printf("接收消息成功:'%s',共%d个字节的数据/n",  
  58.                buffer, len);  
  59.     else  
  60.         printf  
  61.             ("消息接收失败!错误代码是%d,错误信息是'%s'/n",  
  62.              errno, strerror(errno));  
  63.   
  64.     bzero(buffer, MAXBUF + 1);  
  65.     strcpy(buffer, "这是客户端发给服务器端的消息/n");  
  66.     /* 发消息给服务器 */  
  67.     len = send(sockfd, buffer, strlen(buffer), 0);  
  68.     if (len < 0)  
  69.         printf  
  70.             ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'/n",  
  71.              buffer, errno, strerror(errno));  
  72.     else  
  73.         printf("消息'%s'发送成功,共发送了%d个字节!/n",  
  74.                buffer, len);  
  75.   
  76.     /* 关闭连接 */  
  77.     close(sockfd);  
  78.     return 0;  
  79. }  


编译程序用下列命令:
gcc -Wall ipv6-server.c -o ipv6server
gcc -Wall ipv6-client.c -o ipv6client
你自己的主机有IPv6地址吗?很多人会问,输入ifconfig命令看一下吧:

 

 

eth0      链路封装:以太网  硬件地址 00:14:2A:6D:5B:A5  
          inet 地址:192.168.0.167  广播:192.168.0.255  掩码:255.255.255.0
          inet6 地址: fe80::214:2aff:fe6d:5ba5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1
          接收数据包:30507 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:26797 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:1000 
          接收字节:31461154 (30.0 MiB)  发送字节:4472810 (4.2 MiB)
          中断:185 基本地址:0xe400 

lo        链路封装:本地环回  
          inet 地址:127.0.0.1  掩码:255.0.0.0
          inet6 地址: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  跃点数:1
          接收数据包:13 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:13 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:0 
          接收字节:1178 (1.1 KiB)  发送字节:1178 (1.1 KiB)

看到“inet6 地址:”这两行了吗?后面就是你的IPv6地址

启动服务:
./ipv6server 7838 1
或者加上IP地址启动服务:
./ipv6server 7838 1 fe80::214:2aff:fe6d:5ba5

启动客户端测试一下:
./ipv6client ::1/128 7838 

./ipv6client fe80::214:2aff:fe6d:5ba5 7838

 

 

二、UDP ipv6例子

UDP服务端:

  1. #include <unistd.h>  
  2. #include <stdio.h>  
  3. #include <arpa/inet.h>  
  4. #include <sys/socket.h>  
  5. #include <string.h>  
  6. #define LOCALPORT 8888  
  7. int main(int argc,char *argv[])  
  8. {  
  9.     int mysocket,len;  
  10.     int i=0;  
  11.     struct sockaddr_in6 addr;  
  12.     int addr_len;  
  13.     char msg[200];  
  14.     char buf[300];  
  15.   
  16.     if((mysocket=socket(AF_INET6,SOCK_DGRAM,0))<0)  
  17.     {  
  18.         perror("error:");  
  19.         return(1);  
  20.     }  
  21.     else  
  22.     {  
  23.         printf("socket created …\n");  
  24.         printf("socket id :%d \n",mysocket);  
  25.     }  
  26.   
  27.     addr_len=sizeof(struct sockaddr_in6);  
  28.     bzero(&addr,sizeof(addr));  
  29.     addr.sin6_family=AF_INET6;  
  30.     addr.sin6_port=htons(LOCALPORT);  
  31.     addr.sin6_addr=in6addr_any;  
  32.   
  33.     if(bind(mysocket,(struct sockaddr *)&addr,sizeof(addr))<0)  
  34.     {  
  35.         perror("connect");  
  36.         return(1);  
  37.     }  
  38.     else  
  39.     {  
  40.         printf("bink ok .\n");  
  41.         printf("local port : %d\n",LOCALPORT);  
  42.     }  
  43.     while(1)  
  44.     {  
  45.         bzero(msg,sizeof(msg));  
  46.         len = recvfrom(mysocket,msg,sizeof(msg),0,(struct sockaddr *)&addr,(socklen_t*)&addr_len);  
  47.         printf("%d:",i);  
  48.         i++;  
  49.         inet_ntop(AF_INET6,&addr.sin6_addr,buf,sizeof(buf));  
  50.         printf("message from ip %s",buf);  
  51.         printf("Received message : %s\n",msg);  
  52.         if(sendto(mysocket,msg,len,0,(struct sockaddr *)&addr,addr_len)<0)  
  53.         {  
  54.             printf("error");  
  55.             return(1);  
  56.         }  
  57.     }  
  58. }  


UDP客户端代码:

 

  1. #include <stdio.h>  
  2. #include <arpa/inet.h>  
  3. #include <unistd.h>  
  4. #include <sys/socket.h>  
  5. #include <string.h>  
  6. #define REMOTEPORT 8888  
  7. #define REMOTEIP "::1"  
  8. int main(int argc,char *argv[])  
  9. {  
  10.     int mysocket,len;  
  11.     int i=0;  
  12.     struct sockaddr_in6 addr;  
  13.     int addr_len;  
  14.     char msg[200];  
  15.     if((mysocket=socket(AF_INET6,SOCK_DGRAM,0))<0)  
  16.     {  
  17.         perror("error:");  
  18.         return(1);  
  19.     }  
  20.     else  
  21.     {  
  22.         printf("socket created …\n");  
  23.         printf("socket id :%d \n",mysocket);  
  24.         printf("rmote ip : %s\n",REMOTEIP);  
  25.         printf("remote port :%d \n",REMOTEPORT);  
  26.     }  
  27.   
  28.     addr_len=sizeof(struct sockaddr_in6);  
  29.     bzero(&addr,sizeof(addr));  
  30.     addr.sin6_family=AF_INET6;  
  31.     addr.sin6_port=htons(REMOTEPORT);  
  32.     inet_pton(AF_INET6,REMOTEIP,&addr.sin6_addr);  
  33.   
  34.     while(1)  
  35.     {  
  36.         bzero(msg,sizeof(msg));  
  37.         len=read(STDIN_FILENO,msg,sizeof(msg));  
  38.         if(sendto(mysocket,msg,sizeof(msg),0,(struct sockaddr *)&addr,addr_len)<0)  
  39.         {  
  40.             printf("error");  
  41.             return(1);  
  42.         }  
  43.         len=recvfrom(mysocket,msg,sizeof(msg),0,(struct sockaddr *)&addr,(socklen_t*)&addr_len);  
  44.         printf("%d:",i);  
  45.         i++;  
  46.         printf("Received message : %s\n",msg);  
  47.     }  
  48. }  

"::1"相当于ipv4下的lo,即127网段

 

三、ipv6环境下inet_pton和inet_ntop

附上一段ipv6环境下inet_pton和inet_ntop函数代码

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <arpa/inet.h>  
  5.   
  6. int main(int argc, char **argv)  
  7. {  
  8.     unsigned char buf[sizeof(struct in6_addr)];  
  9.     int domain, s;  
  10.     char str[INET6_ADDRSTRLEN];  
  11.   
  12.     if(argc != 3){  
  13.         fprintf(stderr, "usage: %s {i4|i6|<num>} string\n", argv[0]);  
  14.         exit(EXIT_FAILURE);  
  15.     }  
  16.   
  17.     domain = (strcmp(argv[1], “i4”) == 0) ? AF_INET:(strcmp(argv[1], “i6”) == 0) ? AF_INET6 : atoi(argv[1]);  
  18.       
  19.     //IP字符串 ——》网络字节流  
  20.     s = inet_pton(domain, argv[2], buf);  
  21.     if(s<=0)  
  22.     {  
  23.         if(0 == s)  
  24.             fprintf(stderr, "Not in presentation format\n");  
  25.         else  
  26.             perror("inet_pton");  
  27.         exit(EXIT_FAILURE);  
  28.     }  
  29.   
  30.     //网络字节流 ——》IP字符串  
  31.     if(inet_ntop(domain, buf, str, INET6_ADDRSTRLEN) == NULL){    
  32.         perror("inet ntop\n");  
  33.         exit(EXIT_FAILURE);  
  34.     }  
  35.     printf("%s\n", str);  
  36.     exit(EXIT_SUCCESS);  
  37. }  

 

 

 

四、兼容IPV4和IPV6地址代码

为了能够兼容ipv4和ipv6,可以组织代码如下:

  1. #define ADDRESS_BUFFER 50  
  2.   
  3. typedef class address  
  4. {  
  5. private:  
  6.     short int sin_family; //address family AF_INET or AF_INET6  
  7.     union  
  8.     {     
  9.         char binary_addr4[IPV4_LEN];  
  10.         char binary_addr6[IPV6_LEN];  
  11.     }addr;  
  12.     char readable_addr[ADDRESS_BUFFER];  
  13.   
  14. public:  
  15.     address();  
  16.     bool operator == (const address &dst) const;  
  17.     bool operator != (const address &dst) const { return (*this == dst? false : true );}   
  18.     bool operator < (const address &dst) const;  
  19.     const char* get_readable_address() const {return readable_addr;}  
  20.     int get_family() const {return sin_family;}  
  21.     bool is_ipv6() const {return sin_family == AF_INET6;}  
  22.     void set_family(int af) {if(af != AF_INET && af != AF_INET6) return; sin_family = af;}  
  23.     bool set_from_readable_address(const char* readable_address);  
  24.     const char* get_binary_data() {return (char*)&addr;}  
  25. }address;  

 

这里最重要的函数是set_from_readable_address函数,该函数是整个类的入口,执行该函数需要传入一个可读的IP地址,ipv4应该是"xxx.xxx.xxx.xxx"形式,ipv6应该是如:“ff01::1”或者"ffec:afaf::111"等可读的格式,返回false代表转换格式遇到错误,下面贴上该函数的实现代码,其它函数有兴趣的可以自己实现

 

  1. bool address::set_from_readable_address(const char* readable_address)  
  2. {  
  3.     if(readable_address == NULL)  
  4.         return false;  
  5.     memset(addr.binary_addr6, 0, sizeof(addr.binary_addr6));  
  6.     const char*p = readable_address;  
  7.     int cnt = 0;  
  8.     for(; *p != '\0';p++)  
  9.         if(*p == ':')  
  10.             cnt++;  
  11.     if(cnt >= 2)  
  12.     {  
  13.         sin_family = AF_INET6;  
  14.         if( inet_pton(PF_INET6,readable_address,addr.binary_addr6) <= 0)  
  15.             return false;  
  16.         strncpy(readable_addr, readable_address, ADDRESS_BUFFER);  
  17.     }else  
  18.     {  
  19.         sin_family = AF_INET;  
  20.         if( inet_pton(PF_INET,readable_address,addr.binary_addr4) <= 0)  
  21.             return false;  
  22.         strncpy(readable_addr, readable_address, ADDRESS_BUFFER);  
  23.     }  
  24.     return true;  
  25. }  
最后编辑:
作者:liujg
真实-不弄虚,不做假,做自己,不违心; 踏实-不浮躁,不盲从,不急功,不近利; 实学-不投机,不取巧,勤于学,精于业。