如何从一台客户机向服务机发起40亿TCP长连接?
2015-08-19 11:17
507 查看
文章来源:/article/9136142.html
这绝不是标题党!其实这是一篇“科普”文章,讲述了关于“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
plaincopy
+-------------+ +-------------+
| Client Host | | Server Host |
+-------------+ +-------------+
| | A | |
| | /-------port 54000 |
| port 59000/ | |
| |\ | |
| | \-------port 54001 |
| | B | |
+-------------+ +-------------+
从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
plaincopy
// Socket Server
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
using std::memset;
void *SocketHandle(void *_socket)
{
int client_socket = *((int *)_socket);
struct sockaddr_in sa_client;
unsigned int sa_client_len = sizeof(sa_client);
memset(&sa_client, 0x00, sa_client_len);
getpeername(client_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);
char ip[INET_ADDRSTRLEN + 1] = {0};
memset((void *)ip, 0x00, sizeof(ip));
inet_ntop(AF_INET, (void *)&(sa_client.sin_addr), ip, INET_ADDRSTRLEN);
unsigned short int port = ntohs(sa_client.sin_port);
printf("Client %s:%d\n", ip, port);
char recv_buf[2048] = {0};
memset(&recv_buf, 0x00, sizeof(recv_buf));
int recv_size = 0;
while(true)
{
if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)
{
printf("Recv from [%s:%d]:%s\n", ip, port, recv_buf);
memset(&recv_buf, 0x00, sizeof(recv_buf));
if(!strcmp(recv_buf, "close"))
{
break;
}
}
else if(0 == recv_size)
{
printf("Client %s:%d disconnected.\n", ip, port);
fflush(stdout);
break;
}
else // -1
{
printf("Recv failed.\n");
break;
}
}
shutdown(client_socket, SHUT_RDWR);
return (NULL);
}
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Usage: ./SocketServer <listen_port>\n");
return (1);
}
int listen_port = atoi(argv[1]);
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == server_socket)
{
printf("Could not create server socket.\n");
}
int reuse = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
struct sockaddr_in sa_server;
memset(&sa_server, 0x00, sizeof(sa_server));
sa_server.sin_family = AF_INET;
sa_server.sin_addr.s_addr = INADDR_ANY;
sa_server.sin_port = htons(listen_port);
if(bind(server_socket, (struct sockaddr *)&sa_server, sizeof(sa_server)) < 0)
{
printf("Bind server socket fail!\n");
return (1);
}
listen(server_socket, 10);
struct sockaddr_in sa_client;
int sa_client_len = sizeof(sa_client);
memset(&sa_client, 0x00, sa_client_len);
int client_socket = 0;
while(true)
{
client_socket = accept(server_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);
if(client_socket < 0)
{
printf("Accept fail!");
return (1);
}
pthread_t thread_id;
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread_id, &thread_attr, SocketHandle, (void *)&client_socket);
pthread_attr_destroy(&thread_attr);
}
return (0);
}
[cpp] view
plaincopy
// Socket Client
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
if(argc < 4)
{
printf("Usage: ./SocketClient <client_port> <server_ip> <server_port>\n");
return (1);
}
unsigned short int client_port = atoi(argv[1]);
unsigned short int server_port = atoi(argv[3]);
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == client_socket)
{
printf("Create client socket fail.\n");
}
int reuse = 1;
setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
struct sockaddr_in sa_client;
memset((void *)&sa_client, 0x00, sizeof(sa_client));
sa_client.sin_family = AF_INET;
sa_client.sin_port = htons(client_port);
if(bind(client_socket, (struct sockaddr *)&sa_client, sizeof(sa_client)) < 0)
{
printf("bind client socket fail!\n");
return (1);
}
struct sockaddr_in sa_server;
memset(&sa_server, 0x00, sizeof(sa_server));
sa_server.sin_addr.s_addr = inet_addr(argv[2]);
sa_server.sin_family = AF_INET;
sa_server.sin_port = htons(server_port);
if(connect(client_socket, (struct sockaddr *)&sa_server, (socklen_t)sizeof(sa_server)) < 0)
{
printf("Connecting to server fail.\n");
return (1);
}
char send_buf[1024] = {0};
memset(&send_buf, 0x00, sizeof(send_buf));
char recv_buf[2048] = {0};
memset(&recv_buf, 0x00, sizeof(recv_buf));
strcpy(send_buf, "Hello socket server.");
while(true)
{
if(send(client_socket, send_buf, sizeof(send_buf), 0) < 0)
{
printf("Send fail.\n");
fflush(stdout);
break;
}
int recv_size = 0;
if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)
{
printf("Recv:%s\n", recv_buf);
memset(&recv_buf, 0x00, sizeof(recv_buf));
}
else if(0 == recv_size)
{
printf("Client socket disconnected.\n");
fflush(stdout);
break;
}
else // -1
{
printf("Recv failed.\n");
break;
}
}
shutdown(client_socket, SHUT_RDWR);
return (0);
}
验证程序很简单,关键就是 client 端要做 bind 操作,使多个客户端使用相同的的端口发起连接。
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketServer54000.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketServer54001.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketClient59000_54000.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketClient59000_54001.jpg)
从日志运行结果可以看到,54000和54001服务端都收到了从59000端口发起的连接。
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketNetstat.png)
对计算机基础知识的误解,影响是非常深远的。也许一个误解直接就把你的思维限定在一个死胡同里面。也许一个正确而深刻的理解,就能让你想到一个突破性的解决方案。
这绝不是标题党!其实这是一篇“科普”文章,讲述了关于“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
plaincopy
+-------------+ +-------------+
| Client Host | | Server Host |
+-------------+ +-------------+
| | A | |
| | /-------port 54000 |
| port 59000/ | |
| |\ | |
| | \-------port 54001 |
| | B | |
+-------------+ +-------------+
从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
plaincopy
// Socket Server
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
using std::memset;
void *SocketHandle(void *_socket)
{
int client_socket = *((int *)_socket);
struct sockaddr_in sa_client;
unsigned int sa_client_len = sizeof(sa_client);
memset(&sa_client, 0x00, sa_client_len);
getpeername(client_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);
char ip[INET_ADDRSTRLEN + 1] = {0};
memset((void *)ip, 0x00, sizeof(ip));
inet_ntop(AF_INET, (void *)&(sa_client.sin_addr), ip, INET_ADDRSTRLEN);
unsigned short int port = ntohs(sa_client.sin_port);
printf("Client %s:%d\n", ip, port);
char recv_buf[2048] = {0};
memset(&recv_buf, 0x00, sizeof(recv_buf));
int recv_size = 0;
while(true)
{
if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)
{
printf("Recv from [%s:%d]:%s\n", ip, port, recv_buf);
memset(&recv_buf, 0x00, sizeof(recv_buf));
if(!strcmp(recv_buf, "close"))
{
break;
}
}
else if(0 == recv_size)
{
printf("Client %s:%d disconnected.\n", ip, port);
fflush(stdout);
break;
}
else // -1
{
printf("Recv failed.\n");
break;
}
}
shutdown(client_socket, SHUT_RDWR);
return (NULL);
}
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("Usage: ./SocketServer <listen_port>\n");
return (1);
}
int listen_port = atoi(argv[1]);
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == server_socket)
{
printf("Could not create server socket.\n");
}
int reuse = 1;
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
struct sockaddr_in sa_server;
memset(&sa_server, 0x00, sizeof(sa_server));
sa_server.sin_family = AF_INET;
sa_server.sin_addr.s_addr = INADDR_ANY;
sa_server.sin_port = htons(listen_port);
if(bind(server_socket, (struct sockaddr *)&sa_server, sizeof(sa_server)) < 0)
{
printf("Bind server socket fail!\n");
return (1);
}
listen(server_socket, 10);
struct sockaddr_in sa_client;
int sa_client_len = sizeof(sa_client);
memset(&sa_client, 0x00, sa_client_len);
int client_socket = 0;
while(true)
{
client_socket = accept(server_socket, (struct sockaddr *)&sa_client, (socklen_t *)&sa_client_len);
if(client_socket < 0)
{
printf("Accept fail!");
return (1);
}
pthread_t thread_id;
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread_id, &thread_attr, SocketHandle, (void *)&client_socket);
pthread_attr_destroy(&thread_attr);
}
return (0);
}
[cpp] view
plaincopy
// Socket Client
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
if(argc < 4)
{
printf("Usage: ./SocketClient <client_port> <server_ip> <server_port>\n");
return (1);
}
unsigned short int client_port = atoi(argv[1]);
unsigned short int server_port = atoi(argv[3]);
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == client_socket)
{
printf("Create client socket fail.\n");
}
int reuse = 1;
setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
struct sockaddr_in sa_client;
memset((void *)&sa_client, 0x00, sizeof(sa_client));
sa_client.sin_family = AF_INET;
sa_client.sin_port = htons(client_port);
if(bind(client_socket, (struct sockaddr *)&sa_client, sizeof(sa_client)) < 0)
{
printf("bind client socket fail!\n");
return (1);
}
struct sockaddr_in sa_server;
memset(&sa_server, 0x00, sizeof(sa_server));
sa_server.sin_addr.s_addr = inet_addr(argv[2]);
sa_server.sin_family = AF_INET;
sa_server.sin_port = htons(server_port);
if(connect(client_socket, (struct sockaddr *)&sa_server, (socklen_t)sizeof(sa_server)) < 0)
{
printf("Connecting to server fail.\n");
return (1);
}
char send_buf[1024] = {0};
memset(&send_buf, 0x00, sizeof(send_buf));
char recv_buf[2048] = {0};
memset(&recv_buf, 0x00, sizeof(recv_buf));
strcpy(send_buf, "Hello socket server.");
while(true)
{
if(send(client_socket, send_buf, sizeof(send_buf), 0) < 0)
{
printf("Send fail.\n");
fflush(stdout);
break;
}
int recv_size = 0;
if((recv_size = recv(client_socket, recv_buf, 2040, 0)) > 0)
{
printf("Recv:%s\n", recv_buf);
memset(&recv_buf, 0x00, sizeof(recv_buf));
}
else if(0 == recv_size)
{
printf("Client socket disconnected.\n");
fflush(stdout);
break;
}
else // -1
{
printf("Recv failed.\n");
break;
}
}
shutdown(client_socket, SHUT_RDWR);
return (0);
}
验证程序很简单,关键就是 client 端要做 bind 操作,使多个客户端使用相同的的端口发起连接。
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketServer54000.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketServer54001.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketClient59000_54000.jpg)
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketClient59000_54001.jpg)
从日志运行结果可以看到,54000和54001服务端都收到了从59000端口发起的连接。
![](http://blog.yeyuzhen.cn/wp-content/uploads/2014/09/SocketNetstat.png)
对计算机基础知识的误解,影响是非常深远的。也许一个误解直接就把你的思维限定在一个死胡同里面。也许一个正确而深刻的理解,就能让你想到一个突破性的解决方案。
相关文章推荐
- 分享体积小巧,功能强大的网络嗅探和分析工具SnifferView 版本-1.0.0.25
- ios开发判断网络连接及网络异常(ios自学笔记)
- Qt网络获取外网的网络信息
- UVA 10594-Date Flow(无向图的最小费用网络流+题目给的数据有误)
- 利用HttpURLConnection下载文件的核心代码代码
- Tomcat HTTPS
- TCP和UDP的区别(转)
- iOS网络请求
- HTTP协议图片上传交互
- 子网划分的两个例子 怎么算网络号和广播地址
- 8月18日“.我爱你”域名总量:紫田网络排名跌至第十八
- TCP/IP、Http的区别
- 解决 schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/context
- [MFC学习笔记]--网络编程实际操作编写服务器端
- CentOS系统基于网络的PXE+Kickstart无人值守批量安装操作系统(二)
- Android与服务器端数据交互(http协议整合struts2+android)
- [MFC学习笔记]--网络编程理论知识
- TCP建立连接与释放连接过程中的几个问题
- TCP/IP详解 卷I:协议 のping和traceroute
- iOS 网络请求,参数中字典嵌套字典