高并发网络连接数因端口数量受限问题
并发网络连接数因端口数量受限问题
遇到的问题:端口数量受限
一般来说,单独对外提供请求的服务不用考虑端口数量问题,监听某一个端口即可。但是向提供代理服务器,就不得不考虑端口数量受限问题了。当前的1M并发连接测试,也需要在客户端突破6万可用端口的限制。
单机端口上限为65536
端口为16进制,那么2的16次方值为65536,在Linux系统里面,1024以下端口都是超级管理员用户(如root)才可以使用,普通用户只能使用大于1024的端口值。
系统提供了默认的端口范围:
cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
大概也就是共61000-32768=28232个端口可以使用,单个IP对外只能发送28232个TCP请求。
以管理员身份,把端口的范围区间增到最大:
echo "1024 65535"> /proc/sys/net/ipv4/ip_local_port_range
现在有64511个端口可用.
以上做法只是临时,系统下次重启,会还原。 更为稳妥的做法是修改/etc/sysctl.conf文件,增加一行内容
net.ipv4.ip_local_port_range= 1024 65535
保存,然后使之生效:
sysctl -p
现在可以使用的端口达到64510个(假设系统所有运行的服务器是没有占用大于1024的端口的,较为纯净的centos系统可以做到),要想达到50万请求,还得再想办法。
增加IP地址
一般假设本机网卡名称为 eth0,那么手动再添加几个虚拟的IP:
ifconfig eth0:1 192.168.190.151
ifconfig eth0:2 192.168.190.152 ......
或者偷懒一些:
for i in `seq 1 9`; do ifconfig eth0:$i 192.168.190.15$i up ; done
这些虚拟的IP地址,一旦重启,或者 service network restart 就会丢失。
为了模拟较为真实环境,在测试端,手动再次添加9个vmware虚拟机网卡,每一个网卡固定一个IP地址,这样省去每次重启都要重新设置的麻烦。
192.168.190.134
192.168.190.143
192.168.190.144
192.168.190.145
192.168.190.146
192.168.190.147
192.168.190.148
192.168.190.149
192.168.190.150
192.168.190.151
在server服务器端,手动添加桥接网卡和NAT方式网卡
192.168.190.230
192.168.190.240
10.95.20.250
要求测试端和服务器端彼此双方都是可以ping通。
网络四元组/网络五元组
四元组是指的是
{源IP地址,源端口,目的IP地址,目的端口}
五元组指的是(多了协议)
{源IP地址,目的IP地址,协议号,源端口,目的端口}
一个TCP连接的套接字对(socket pari)是一个定义该连接的两个端点的四元组,即本地IP地址、本地TCP端口号、外地IP地址、外地TCP端口号。套接字对唯一标识一个网络上的每个TCP连接。
......
标识每个端点的两个值(IP地址和端口号)通常称为一个套接字。
以下以四元组为准。在测试端四元组可以这样认为:
{本机IP地址,本机端口,目的IP地址,目的端口}
请求的IP地址和目的端口基本上是固定的,不会变化,那么只能从本机IP地址和本机端口上考虑,端口的范围一旦指定了,那么增加IP地址,可以增加对外发出的请求数量。假设系统可以使用的端口范围已经如上所设,那么可以使用的大致端口为64000个,系统添加了10个IP地址,那么可以对外发出的数量为 64000 * 10 = 640000,数量很可观。
只有{源IP地址,源端口}确定对外TCP请求数量
经测试,四元组里面,只有{源IP地址,源端口}才能够确定对外发出请求的数量,跟{目的IP地址,目的端口}无关。
测试环境
在server端,并且启动./server两次,分别绑定8000端口和9000端口
./server -p 8000
./server -p 9000
本机IP、端口绑定测试程序
这里写一个简单的测试绑定本机IP地址和指定端口的客户端测试程序。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
#include <sys/types.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <err.h>
#include <event.h>
#include <evhttp.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <pthread.h>
#include <errno.h>
#define BUFSIZE 4096
#define SLEEP_MS 10
char buf[BUFSIZE];
int bytes_recvd = 0;
int chunks_recvd = 0;
void chunkcb(struct evhttp_request *req, void *arg) {
int s = evbuffer_remove( req->input_buffer, &buf, BUFSIZE );
bytes_recvd += s;
chunks_recvd++;
printf(">Chunks: %d\tBytes: %d\n", chunks_recvd, bytes_recvd);
}
void reqcb(struct evhttp_request *req, void *arg) {
fprintf(stderr, ">Now closed\n");
exit(-1);
}
void err_cb(int err){
fprintf(stderr, "setup failed(errno = %d): %s", errno, strerror(errno));
}
int main(int argc, char **argv) {
char server_ip[16] = "";
int server_port = 0;
char local_ip[16] = "";
int local_port = 0;
int ch;
while ((ch = getopt(argc, argv, "h:p:c:o:")) != -1) {
switch (ch) {
case 'h':
printf("remote host is %s\n", optarg);
strncpy(server_ip, optarg, 15);
break;
case 'p':
printf("remote port is %s\n", optarg);
server_port = atoi(optarg);
break;
case 'c':
printf("local ip is %s\n", optarg);
strncpy(local_ip, optarg, 15);
break;
case 'o':
printf("local port is %s\n", optarg);
local_port = atoi(optarg);
break;
}
}
event_init();
event_set_fatal_callback(err_cb);
struct evhttp *evhttp_connection;
struct evhttp_request *evhttp_request;
char path[32];
evhttp_connection = evhttp_connection_new(server_ip, server_port);
evhttp_connection_set_local_address(evhttp_connection, local_ip);
evhttp_connection_set_local_port(evhttp_connection, local_port);
evhttp_set_timeout(evhttp_connection, 864000); // 10 day timeout
evhttp_request = evhttp_request_new(reqcb, NULL);
evhttp_request->chunk_cb = chunkcb;
sprintf(&path, "/test/%d", local_port);
evhttp_make_request( evhttp_connection, evhttp_request, EVHTTP_REQ_GET, path );
evhttp_connection_set_timeout(evhttp_request->evcon, 864000);
event_loop( EVLOOP_NONBLOCK );
usleep(SLEEP_MS * 10);
event_dispatch();
return 0;
}
|
可以看到libevent-*/include/event2/http.h内置了对绑定本地IP地址的支持:
/** sets the ip address from which http connections are made */
void evhttp_connection_set_local_address(struct evhttp_connection *evcon,
const char *address);
不用担心端口,系统自动自动随机挑选,除非需要特别指定:
/** sets the local port from which http connections are made */
void evhttp_connection_set_local_port(struct evhttp_connection *evcon,
ev_uint16_t port);
编译
gcc -o client3 client3.c -levent
client3运行参数为
- -h 远程主机IP地址
- -p 远程主机端口
- -c 本机指定的IP地址(必须可用)
- -o 本机指定的端口(必须可用)
测试用例,本机指定同样的IP地址和端口,但远程主机和IP不一样.
在一个测试端打开一个终端窗口1,切换到 client3对应位置
./client3 -h 192.168.190.230 -p 8000 -c 192.168.190.148 -o 4000
输出为
remote host is 192.168.190.230
remote port is 8000
local ip is 192.168.190.148
local port is 4000
>Chunks: 1 Bytes: 505
再打开一个测试端终端窗口2,执行:
./client3 -h 192.168.190.240 -p 9000 -c 192.168.190.148 -o 4000
窗口2程序,无法执行,自动退出。
接着在窗口2终端继续输入:
./client3 -h 192.168.190.230 -p 8000 -c 192.168.190.148 -o 4001
注意,和窗口1相比,仅仅改变了端口号为4001。但执行结果,和端口1输出一模一样,在等待接收数据,没有自动退出。
剩下的,无论怎么组合,怎么折腾,只要一对{本机IP,本机端口}被占用,也就意味着对应一个具体的文件句柄,那么其它程序将不能够再次使用。
Java怎么绑定本地IP地址?
Java绑定就很简单,但有些限制,不够灵活,单纯从源码中看不出来,api doc可以告诉我们一些事情。 打开JDKAPI1_6zhCN.CHM,查看InetSocketAddress类的构造函数说明:
public InetSocketAddress(InetAddress addr, int port)
根据 IP 地址和端口号创建套接字地址。 有效端口值介于 0 和 65535 之间。端口号 zero 允许系统在 bind 操作中挑选暂时的端口。null 地址将分配通配符 地址。
参数:
addr - IP 地址
port - 端口号
抛出:
IllegalArgumentException - 如果 port 参数超出有效端口值的指定范围。
public InetSocketAddress(String hostname, int port)
根据主机名和端口号创建套接字地址。
尝试将主机名解析为 InetAddress。如果尝试失败,则将地址标记为未解析。如果存在安全管理器,则将主机名用作参数调用其 checkConnect 方法,以检查解析它的权限。这可能会导致 SecurityException 异常。
有效端口值介于 0 和 65535 之间。端口号 zero 允许系统在 bind 操作中挑选暂时的端口。
参数: hostname - 主机名
port - 端口号
抛出:
IllegalArgumentException - 如果 port 参数超出有效端口值的范围,或者主机名参数为 null。
SecurityException - 如果存在安全管理器,但拒绝解析主机名的权限。
另请参见:
isUnresolved()
InetSocketAddress的两个构造函数都支持,看情况使用。注意int port传递值为0,即可做到系统随机挑选端口。追踪一下源代码,发现最终调用
private native void socketBind(InetAddress address, int port) throws IOException;
如何查看socketBind的原始C代码,我就不清楚了,您若知晓,希望指教一下。 构造一个InetSocketAddress对象:
SocketAddress localSocketAddr = new InetSocketAddress("192.168.190.143", 0);
然后传递给需要位置即可。诸如使用netty连接到某个服务器上,在connect时指定远方地址,以及本机地址
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) Attempts a new connection with the specified remoteAddress and the specified localAddress.
Netty 客户端连接API见: http://docs.jboss.org/netty/3.2/api/org/jboss/netty/bootstrap/ClientBootstrap.html
Linux支持绑定本机IP、端口原理
说是原理,有些牵强,因为linux C提供了如何绑定函数,框架或者高级语言再怎么封装,在linux平台下面,需要这么调用:
struct sockaddr_in clnt_addr;
....
clnt_addr.sin_family = AF_INET;
clnt_addr.sin_addr.s_addr = INADDR_ANY; //绑定本机IP地址
clnt_addr.sin_port = htons(33333); //绑定本机端口
if (bind(sockfd, (struct sockaddr *) &clnt_addr,
sizeof(clnt_addr)) < 0) error("ERROR on binding");
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) error("ERROR connecting");
.......
构造一个clnt_addr结构体,本地IP或者端口赋值,在connect之前,先bind,就这么简单。
更完整例子,可以参考 http://stackoverflow.com/questions/4852256/need-a-complete-snippet-example-of-binding-tcp-client-socket
高并发网络连接数因端口数量受限问题相关推荐
- 100万并发连接服务器笔记之处理端口数量受限问题
第二个遇到的问题:端口数量受限 一般来说,单独对外提供请求的服务不用考虑端口数量问题,监听某一个端口即可.但是向提供代理服务器,就不得不考虑端口数量受限问题了.当前的1M并发连接测试,也需要在客户端突 ...
- 高并发网络编程之epoll详解
在linux 没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序.在大数据.高并发.集群等一些名词唱得火热之年代,select和poll的 ...
- 高并发网络编程之epoll(个人遇到最好理解的一篇文章、易懂)
LT 和 ET本质的区别是: LT模式状态时,主线程正在epoll_wait等待事件时,请求到了,epoll_wait返回后没有去处理请求(recv),那么下次epoll_wait时此请求还是会返回( ...
- 高并发网络编程之NIO非阻塞网络编程
文章目录 NIO非阻塞网络编程 Buffer缓冲区 Buffer工作原理: ByteBuffer内存类型 Channel通道 SocketChannel ServerSocketChannel Sel ...
- mongodb线程池_常用高并发网络线程模型设计及MongoDB线程模型优化实践
服务端通常需要支持高并发业务访问,如何设计优秀的服务端网络IO工作线程/进程模型对业务的高并发访问需求起着至关重要的核心作用. 本文总结了了不同场景下的多种网络IO线程/进程模型,并给出了各种模型的优 ...
- 高并发网络架构解决方案分析
1:html静态化 2:图片服务器分离 3:数据库集群 4:缓存 5:负载均衡 大型高并发高负载网站的系统架构 我在Cernet做过拨号接入平台的搭建,而后在Yahoo3721负载搜索引擎前端平台开发 ...
- 高并发网络服务器设计
小白模式 毕业入职第一家公司的入职作业就是写一个高并发的http服务器.当时的大致思路是这样: 主线程创建监听端口,将监听端口放入epoll监听列表,然后epoll开始循环监听,当到来的读请求是监听端 ...
- nodeJS express mysql 高并发时连接数不够用问题 以及如何处理高并发
首先 描述下问题,前段时间接到了通知,做nodejs高并发代码优化,于是开始整咯,首先用loadrunning模拟高并发,问题就来了,到高并发路由的时候,会出现数据库连接数不够用的情况.查询了代码,都 ...
- libevent c++高并发网络编程_高并发编程学习(2)——线程通信详解
前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...
最新文章
- swim 中一行代码解决收回键盘
- 是时候装逼了,试试 IDEA 解决 Maven 依赖冲突的高能神器!
- 拿下丰厚的年终奖,却未能拯救总薪酬,2021 年度 IT 薪酬调查报告出炉!
- python 信号量,Event, 定时器
- Jmeter操作mysql数据库测试
- centos一键清理磁盘空间_如何清理 Docker 占用的磁盘空间
- 【JavaScript】appendChild一个的注意点之会删除原dom树节点
- python中在同一个位置输出数据
- Linux下使用脚本安装和升级pip
- 关于CodeReview
- Maven安装和配置详细教程
- web压力测试的几个指标
- photoshopCS6软件的安装和破解方法
- 为什么拉格朗日对偶函数一定是凹函数(逐点下确界)
- 交换机级联后网速在底层交换机变慢的问题
- intellij idea实现代码实时翻译的插件开发
- 网络编程 —— 基础理论知识
- 上海迪士尼将推出虎年新春全新体验
- AS608指纹模块的上位机检测
- android studio实验二 Activity及常用布局和控件的使用
热门文章
- matlab 函数,matlab 语法1
- 计算机PPT中项目编号怎么弄,电脑技巧收藏家电脑基础设置幻灯片格式:项目符号和编号...
- SIP注册信令消息示范及解释
- java计算机毕业设计培训机构运营系统源码+程序+lw文档+mysql数据库
- 网络协议 -- ARP和RARP协议
- [W ParallelNative.cpp:212] Warning: Cannot set number of intraop threads after parallel work h
- linux 内核 修改mss,[转载]linux 内核对于TCPMSS的处理
- 图解Go语言内存分配 https://juejin.im/post/5c888a79e51d456ed11955a8
- 洛谷 P1460 健康的荷斯坦奶牛 Healthy Holsteins
- 操作系统-消费者生产者代码C++Windows实现