如何从一台客户机向服务机发起40亿TCP长连接?

文章来源:http://blog.csdn.net/lostaway/article/details/39451701

这绝不是标题党!其实这是一篇“科普”文章,讲述了关于“socket=((client_ip:client_port)-(server_ip:server_port),pocotol)”的一个普遍的误解。

       因为带着这样的误解,我将曾经的一个测试任务变成了“社交活动”。为了测试新开发出来的TCP长连接服务器的性能,我们需要让一台测试服务机保持100W+的长连接。一台客户机能发起的长连接数最大为65535(这是常识!)。经过小组讨论,以一台客户机发起6W长连接来算,我们需要找到 17 台测试客户机。于是我们将测试服务器搭在了公司内网仅有的一台服务器上。写了一个windows下的benchmark程序,通过各种“社交渠道”分发了个同事。功夫不负有心人,总算压到了100W+以上的TCP长连接。so happy~~~

       回头来总结那次测试任务——我们带着对socket的误解,集体傻B了一回。

       实际上仔细推敲一下socket的定义,我们就可以发现一台客户机向一台服务机能发起的最大TCP长连接数应该是40亿+(65535*65535),而不是普遍认为的65535!因为:

[plain]  view plain copy
  1. +-------------+         +-------------+  
  2. | Client Host |         | Server Host |  
  3. +-------------+         +-------------+  
  4. |             |    A    |             |  
  5. |             | /-------port 54000    |  
  6. |    port 59000/        |             |  
  7. |             |\        |             |  
  8. |             | \-------port 54001    |  
  9. |             |    B    |             |  
  10. +-------------+         +-------------+  

从socket的定义“socket=((client_ip:client_port)-(server_ip:server_port),pocotol)”我们可以知道A和B实际上是不同的连接,这说明了一个客户机的端口是可以连接到服务机的不同端口的。如果 Server Host 开启 65535 个服务端监听端口,那么一个 Client Host 的端口就可以发起 65535 个连接。Client Host 总计有 65535 个端口,所以理论上客户机能够向服务机发起的最大连接数应该是 65535*65535≈40亿+。

让我们来实际验证一下:

[cpp]  view plain copy
  1. // Socket Server  
  2. #include <iostream>  
  3. #include <cstdio>  
  4. #include <cstdlib>  
  5. #include <cstring>  
  6.   
  7. #include <sys/socket.h>  
  8. #include <arpa/inet.h>  
  9. #include <netinet/in.h>  
  10.   
  11. #include <pthread.h>  
  12.   
  13. using std::memset;  
  14.   
  15. void *SocketHandle(void *_socket)  
  16. {  
  17.     int client_socket = *((int *)_socket);  
  18.   
  19.     struct sockaddr_in sa_client;  
  20.     unsigned int sa_client_len = sizeof(sa_client);  
  21.     memset(&sa_client, 0x00, sa_client_len);  
  22.   
  23.     getpeername(client_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);  
  24.     char ip[INET_ADDRSTRLEN + 1] = {0};  
  25.     memset((void *)ip, 0x00, sizeof(ip));  
  26.     inet_ntop(AF_INET, (void *)&(sa_client.sin_addr), ip, INET_ADDRSTRLEN);  
  27.     unsigned short int port = ntohs(sa_client.sin_port);  
  28.     printf("Client %s:%d\n", ip, port);  
  29.   
  30.     char recv_buf[2048] = {0};  
  31.     memset(&recv_buf, 0x00, sizeof(recv_buf));  
  32.     int recv_size = 0;  
  33.   
  34.     while(true)  
  35.     {  
  36.         if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)  
  37.         {  
  38.             printf("Recv from [%s:%d]:%s\n", ip, port, recv_buf);  
  39.             memset(&recv_buf, 0x00, sizeof(recv_buf));  
  40.             if(!strcmp(recv_buf, "close"))  
  41.             {  
  42.                 break;  
  43.             }  
  44.         }  
  45.         else if(0 == recv_size)  
  46.         {  
  47.             printf("Client %s:%d disconnected.\n", ip, port);  
  48.             fflush(stdout);  
  49.             break;  
  50.         }  
  51.         else // -1  
  52.         {  
  53.             printf("Recv failed.\n");  
  54.             break;  
  55.         }  
  56.     }  
  57.     shutdown(client_socket, SHUT_RDWR);  
  58.   
  59.     return (NULL);  
  60. }  
  61.   
  62. int main(int argc, char *argv[])  
  63. {  
  64.     if(argc < 2)  
  65.     {  
  66.         printf("Usage: ./SocketServer <listen_port>\n");  
  67.         return (1);  
  68.     }  
  69.   
  70.     int listen_port = atoi(argv[1]);  
  71.     int server_socket = socket(AF_INET, SOCK_STREAM, 0);  
  72.     if(-1 == server_socket)  
  73.     {  
  74.         printf("Could not create server socket.\n");  
  75.     }  
  76.     int reuse = 1;  
  77.     setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));  
  78.   
  79.     struct sockaddr_in sa_server;  
  80.     memset(&sa_server, 0x00, sizeof(sa_server));  
  81.     sa_server.sin_family = AF_INET;  
  82.     sa_server.sin_addr.s_addr = INADDR_ANY;  
  83.     sa_server.sin_port = htons(listen_port);  
  84.   
  85.     if(bind(server_socket, (struct sockaddr *)&sa_server, sizeof(sa_server)) < 0)  
  86.     {  
  87.         printf("Bind server socket fail!\n");  
  88.         return (1);  
  89.     }  
  90.   
  91.     listen(server_socket, 10);  
  92.   
  93.     struct sockaddr_in sa_client;  
  94.     int sa_client_len = sizeof(sa_client);  
  95.     memset(&sa_client, 0x00, sa_client_len);  
  96.   
  97.     int client_socket = 0;  
  98.     while(true)  
  99.     {  
  100.         client_socket = accept(server_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);  
  101.         if(client_socket < 0)  
  102.         {  
  103.             printf("Accept fail!");  
  104.             return (1);  
  105.         }  
  106.   
  107.         pthread_t thread_id;  
  108.         pthread_attr_t thread_attr;  
  109.         pthread_attr_init(&thread_attr);  
  110.         pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);  
  111.         pthread_create(&thread_id, &thread_attr, SocketHandle, (void *)&client_socket);  
  112.         pthread_attr_destroy(&thread_attr);  
  113.     }  
  114.   
  115.     return (0);  
  116. }  
[cpp]  view plain copy
  1. // Socket Client  
  2. #include <cstdio>  
  3. #include <cstdlib>  
  4. #include <cstring>  
  5. #include <unistd.h>  
  6. #include <sys/socket.h>  
  7. #include <arpa/inet.h>  
  8.   
  9. int main(int argc, char *argv[])  
  10. {  
  11.     if(argc < 4)  
  12.     {  
  13.         printf("Usage: ./SocketClient <client_port> <server_ip> <server_port>\n");  
  14.         return (1);  
  15.     }  
  16.     unsigned short int client_port = atoi(argv[1]);  
  17.     unsigned short int server_port = atoi(argv[3]);  
  18.   
  19.     int client_socket = socket(AF_INET, SOCK_STREAM, 0);  
  20.     if(-1 == client_socket)  
  21.     {  
  22.         printf("Create client socket fail.\n");  
  23.     }  
  24.     int reuse = 1;  
  25.     setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));  
  26.   
  27.     struct sockaddr_in sa_client;  
  28.     memset((void *)&sa_client, 0x00, sizeof(sa_client));  
  29.     sa_client.sin_family = AF_INET;  
  30.     sa_client.sin_port = htons(client_port);  
  31.   
  32.     if(bind(client_socket, (struct sockaddr *)&sa_client, sizeof(sa_client)) < 0)  
  33.     {  
  34.         printf("bind client socket fail!\n");  
  35.         return (1);  
  36.     }  
  37.   
  38.     struct sockaddr_in sa_server;  
  39.     memset(&sa_server, 0x00, sizeof(sa_server));  
  40.     sa_server.sin_addr.s_addr = inet_addr(argv[2]);  
  41.     sa_server.sin_family = AF_INET;  
  42.     sa_server.sin_port = htons(server_port);  
  43.   
  44.     if(connect(client_socket, (struct sockaddr *)&sa_server, (socklen_t)sizeof(sa_server)) < 0)  
  45.     {  
  46.         printf("Connecting to server fail.\n");  
  47.         return (1);  
  48.     }  
  49.   
  50.     char send_buf[1024] = {0};  
  51.     memset(&send_buf, 0x00, sizeof(send_buf));  
  52.     char recv_buf[2048] = {0};  
  53.     memset(&recv_buf, 0x00, sizeof(recv_buf));  
  54.   
  55.     strcpy(send_buf, "Hello socket server.");  
  56.   
  57.     while(true)  
  58.     {  
  59.         if(send(client_socket, send_buf, sizeof(send_buf), 0) < 0)  
  60.         {  
  61.             printf("Send fail.\n");  
  62.             fflush(stdout);  
  63.             break;  
  64.         }  
  65.   
  66.         int recv_size = 0;  
  67.         if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)  
  68.         {  
  69.             printf("Recv:%s\n", recv_buf);  
  70.             memset(&recv_buf, 0x00, sizeof(recv_buf));  
  71.         }  
  72.         else if(0 == recv_size)  
  73.         {  
  74.             printf("Client socket disconnected.\n");  
  75.             fflush(stdout);  
  76.             break;  
  77.         }  
  78.         else // -1  
  79.         {  
  80.             printf("Recv failed.\n");  
  81.             break;  
  82.         }  
  83.     }  
  84.   
  85.     shutdown(client_socket, SHUT_RDWR);  
  86.     return (0);  
  87. }  

验证程序很简单,关键就是 client 端要做 bind 操作,使多个客户端使用相同的的端口发起连接。

从日志运行结果可以看到,54000和54001服务端都收到了从59000端口发起的连接。

对计算机基础知识的误解,影响是非常深远的。也许一个误解直接就把你的思维限定在一个死胡同里面。也许一个正确而深刻的理解,就能让你想到一个突破性的解决方案。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值