C++ IPv4与IPv6的兼容编码

这里不再对IPv6 socket相关编程的基础知识进行讲解,只提供一个IP协议无关的服务端和客户端的代码,仅供参考。

服务端代码:

  1. #include <iostream>
  2. #include <string>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <arpa/inet.h>
  6. #include <netdb.h>
  7. #include <errno.h>
  8. #include <time.h>
  9. using namespace std;
  10. int tcp_listen(const char *host, const char *service, const int listen_num = 5)
  11. {
  12. int listenfd, ret;
  13. const int on = 1;
  14. struct addrinfo hints, *res, *ressave;
  15. bzero(&hints, sizeof(hints));
  16. hints.ai_flags = AI_PASSIVE;
  17. hints.ai_family = AF_UNSPEC;
  18. hints.ai_socktype = SOCK_STREAM;
  19. hints.ai_protocol = IPPROTO_IP;
  20. if (0 != (ret = getaddrinfo(host, service, &hints, &res)))
  21. {
  22. cout << "getaddrinfo error: " << gai_strerror(ret) << endl;
  23. return -1;
  24. }
  25. ressave = res;
  26. while(NULL != res)
  27. {
  28. if (-1 == (listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))
  29. {
  30. cout << "create socket error: " << strerror(errno) << endl;
  31. res = res->ai_next;
  32. continue;
  33. }
  34. if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
  35. {
  36. cout << "setsockopt error: " << strerror(errno) << endl;
  37. close(listenfd);
  38. res = res->ai_next;
  39. continue;
  40. }
  41. if (-1 == bind(listenfd, res->ai_addr, res->ai_addrlen))
  42. {
  43. cout << "bind error: " << strerror(errno) << endl;
  44. close(listenfd);
  45. res = res->ai_next;
  46. continue;
  47. }
  48. if (-1 == listen(listenfd, listen_num))
  49. {
  50. cout << "listen error: " << strerror(errno) << endl;
  51. close(listenfd);
  52. res = res->ai_next;
  53. continue;
  54. }
  55. break;
  56. }
  57. freeaddrinfo(ressave);
  58. if (NULL == res)
  59. return -1;
  60. return listenfd;
  61. }
  62. int get_addrinfo(const struct sockaddr *addr, string &ip, in_port_t &port)
  63. {
  64. void *numeric_addr = NULL;
  65. char addr_buff[INET6_ADDRSTRLEN];
  66. if (AF_INET == addr->sa_family)
  67. {
  68. numeric_addr = &((struct sockaddr_in*)addr)->sin_addr;
  69. port = ntohs(((struct sockaddr_in*)addr)->sin_port);
  70. }
  71. else if (AF_INET6 == addr->sa_family)
  72. {
  73. numeric_addr = &((struct sockaddr_in6*)addr)->sin6_addr;
  74. port = ntohs(((struct sockaddr_in6*)addr)->sin6_port);
  75. }
  76. else
  77. {
  78. return -1;
  79. }
  80. if (NULL != inet_ntop(addr->sa_family, numeric_addr, addr_buff, sizeof(addr_buff)))
  81. ip = addr_buff;
  82. else
  83. return -1;
  84. return 0;
  85. }
  86. int main(int argc, char *argv[])
  87. {
  88. int listenfd, connfd;
  89. struct sockaddr_storage cliaddr;
  90. socklen_t len = sizeof(cliaddr);
  91. time_t now;
  92. char buff[128];
  93. if (2 == argc) //指定端口
  94. listenfd = tcp_listen(NULL, argv[1]);
  95. else if (3 == argc) //指定本地IP和端口
  96. listenfd = tcp_listen(argv[1], argv[2]);
  97. else
  98. {
  99. cout << "usage: " << argv[0] << " [<hostname/ipaddress>] <service/port>" << endl;
  100. return -1;
  101. }
  102. if (listenfd < 0)
  103. {
  104. cout << "call tcp_listen error" << endl;
  105. return -1;
  106. }
  107. while (true)
  108. {
  109. connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);
  110. string ip = "";
  111. in_port_t port = 0;
  112. get_addrinfo((struct sockaddr*)&cliaddr, ip, port);
  113. cout << "client " << ip << "|" << port << " login" << endl;
  114. now = time(NULL);
  115. snprintf(buff, sizeof(buff) - 1, "%.24s", ctime(&now));
  116. write(connfd, buff, strlen(buff));
  117. close(connfd);
  118. }
  119. close(listenfd);
  120. return 0;
  121. }


客户端代码:

  1. #include <iostream>
  2. #include <string>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <arpa/inet.h>
  6. #include <netdb.h>
  7. #include <errno.h>
  8. #include <time.h>
  9. using namespace std;
  10. int tcp_connect(const char *host, const char *service)
  11. {
  12. int sockfd, ret;
  13. struct addrinfo hints, *res, *ressave;
  14. bzero(&hints, sizeof(hints));
  15. hints.ai_family = AF_UNSPEC;
  16. hints.ai_socktype = SOCK_STREAM;
  17. hints.ai_protocol = IPPROTO_IP;
  18. if (0 != (ret = getaddrinfo(host, service, &hints, &res)))
  19. {
  20. cout << "getaddrinfo error: " << gai_strerror(ret) << endl;
  21. return -1;
  22. }
  23. ressave = res;
  24. while (NULL != res)
  25. {
  26. if (-1 == (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))
  27. {
  28. cout << "create socket error: " << strerror(errno) << endl;
  29. res = res->ai_next;
  30. continue;
  31. }
  32. if (-1 == connect(sockfd, res->ai_addr, res->ai_addrlen))
  33. {
  34. cout << "connect error: " << strerror(errno) << endl;
  35. close(sockfd);
  36. res = res->ai_next;
  37. continue;
  38. }
  39. break;
  40. }
  41. freeaddrinfo(ressave);
  42. if (NULL == res)
  43. return -1;
  44. return sockfd;
  45. }
  46. int main(int argc, char *argv[])
  47. {
  48. int sockfd, n;
  49. char buff[128];
  50. struct sockaddr_storage cliaddr;
  51. if (3 != argc)
  52. {
  53. cout << "usage: " << argv[0] << " <hostname/ipaddress> <service/port>" << endl;
  54. return -1;
  55. }
  56. sockfd = tcp_connect(argv[1], argv[2]);
  57. if (sockfd < 0)
  58. {
  59. cout << "call tcp_connect error" << endl;
  60. return -1;
  61. }
  62. bzero(buff, sizeof(buff));
  63. while ((n = read(sockfd, buff, sizeof(buff) - 1) > 0))
  64. {
  65. cout << buff << endl;
  66. bzero(buff, sizeof(buff));
  67. }
  68. close(sockfd);
  69. return 0;
  70. }


编译:

g++ daytimesrv.cpp -o daytimesrv

g++ daytimecli.cpp -o daytimecli


运行举例:

服务端运行情况:

C++ IPv4与IPv6的兼容编码C++ IPv4与IPv6的兼容编码


客户端运行情况:

C++ IPv4与IPv6的兼容编码C++ IPv4与IPv6的兼容编码


以上客户端的请求与服务端的输出一一对应,客户端可以尝试用不同的IP去连接,然后观察服务端的输出,有助于理解底层的交互过程。来源:https://blog.****.net/ligt0610/article/details/18667595#