1.TCP链接(短连接和长链接)html
什么是TCP链接?TCP(Transmission Control Protocol 传输控制协议)是一种面向链接的、可靠的、基于字节流的传输层通讯协议。linux
当网络通讯时采用TCP协议时,在真正的读写操做以前,server与client之间必须创建一个链接,当读写操做完成后,双方再也不须要这个链接时它们能够释放这个链接,链接的创建是须要三次握手的,而释放则须要4次挥手,因此说每一个链接的创建都是须要资源消耗和时间消耗的。git
TCP短连接:短链接是指通讯双方有数据交互时,就创建一个TCP链接,数据发送完成后,则断开此TCP链接(管理起来比较简单,存在的链接都是有用的链接,不须要额外的控制手段)。github
链接→数据传输→关闭链接;数据库
TCP长链接:所谓长链接,指在一个TCP链接上能够连续发送多个数据包,在TCP链接保持期间,若是没有数据包发送,须要双方发检测包以维持此链接,通常须要本身作在线维持(不发生RST(reset)包用于强制关闭TCP链接的包和四次挥手)。编程
链接→数据传输→保持链接(心跳)→数据传输→保持链接(心跳)→……→关闭链接(一个TCP链接通道多个读写通讯);vim
这就要求长链接在没有数据通讯时,定时发送数据包(心跳),以维持链接状态。这就是TCP保活功能,保活功能主要为服务器应用提供,服务器应用但愿知道客户主机是否崩溃,从而能够释放客户端占用的资源。服务器
心跳包:它像心跳同样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。用于保持长链接,至于这个包的内容,是没有什么特别规定的,不过通常都是很小的包,或者只包含包头的一个空包。在TCP的机制里面,自己是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。可是它检查不到机器断电、网线拔出、防火墙这些断线。心跳包主要也就是用于长链接的保活和断线处理。通常的应用下,断定时间在30-40秒比较不错。若是实在要求高,那就在6-9秒。网络
应用场景:长链接多用于操做频繁(读写),点对点的通信,并且链接数不能太多状况。每一个TCP链接都须要三步握手,这须要时间,若是每一个操做都是先链接,再操做的话那么处理速度会下降不少,因此每一个操做完后都不断开,下次处理时直接发送数据包就OK了,不用创建TCP链接。例如:数据库的链接用长链接, 若是用短链接频繁的通讯会形成socket错误,并且频繁的socket 建立也是对资源的浪费。并发
而像WEB网站的http服务通常都用短连接(http1.0只支持短链接,http1.1keep alive 带时间,操做次数限制的长链接),由于长链接对于服务端来讲会耗费必定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的链接用短链接会更省一些资源,若是用长链接,并且同时有成千上万的用户,若是每一个用户都占用一个链接的话,那可想而知吧。因此并发量大,但每一个用户无需频繁操做状况下需用短连好;
在长链接中通常是没有条件可以判断读写何时结束,因此必需要加长度报文头。读函数先是读取报文头的长度,以及报文开始结束符号,再根据这个长度去读相应长度的报文。
2.单台服务器支持最大的TCP链接数
在tcp应用中,server事先在某个固定端口监听,client主动发起链接,通过三路握手后创建tcp链接。那么对单机,其最大并发tcp链接数是多少?
如何标识一个TCP链接:系统用一个4四元组来惟一标识一个TCP链接:{local ip, local port,remote ip,remote port}。
Client最大tcp链接数:client每次发起tcp链接请求时,除非绑定端口,一般会让系统选取一个空闲的本地端口(local port),该端口是独占的,不能和其余tcp链接共享。tcp端口的数据类型是unsigned short,所以本地端口个数最大只有65536,端口0有特殊含义,不能使用,这样可用端口最多只有65535,因此在所有做为client端的状况下,客户端发起的最大tcp链接数为65535,这些链接能够连到不一样的server ip。
Server最大tcp链接数:server一般固定在某个本地端口上监听,等待client的链接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的状况下,即便server端有多个ip,本地监听端口也是独占的,所以server端tcp链接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,所以最大tcp链接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp链接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp链接数约为2的48次方。
实际的tcp链接数:上面给出的是理论上的单机最大链接数,在实际环境中,受到机器资源、操做系统等的限制,特别是sever端,其最大并发tcp链接数远不能达到理论上限。在unix/linux下限制链接数的主要因素是内存和容许的文件描述符个数(每一个tcp链接都要占用必定内存,每一个socket就是一个文件描述符),另外1024如下的端口一般为保留端口。在默认2.6内核配置下,通过试验,每一个socket占用内存在15~20k之间。
对server端,经过增长内存、修改最大文件描述符个数等参数,单机最大并发TCP链接数超过10万 是没问题的,国外 Urban Airship 公司在产品环境中已作到 50 万并发 。在实际应用中,对大规模网络应用,还须要考虑C10K 问题。咱们先假设单台服务器最多只能支持万级并发链接,其实对绝大多数应用来讲已经远远足够了,可是对于一些拥有很大用户基数的互联网公司,每每面临的并发链接数是百万,千万,甚至腾讯的上亿(注:QQ默认用的UDP协议)。虽然如今的集群,分布式技术能够为咱们将并发负载分担在多台服务器上,那咱们只须要扩展出数十台电脑就能够解决问题,可是咱们更但愿能更大的挖掘单台服务器的资源,先努力垂直扩展,再进行水平扩展,这样能够有效的节省服务器相关的开支(硬件资源,机房,运维,电力其实也是一笔不小的开支)。
补充知识:
在linux下编写网络服务器程序时每个tcp链接都要占一个文件描述符,一旦这个文件描述符使用完了,新的链接到来返回给咱们的错误是“Socket/File:Can't open so many files”即文件打开过多!。
这时你须要明白操做系统对能够打开的最大文件数的限制。
进程限制
执行 ulimit -n 输出 1024,说明对于一个进程而言最多只能打开1024个文件,因此你要采用此默认配置最多也就能够并发上千个TCP链接。
临时修改:ulimit -n 1000000,可是这种临时修改只对当前登陆用户目前的使用环境有效,系统重启或用户退出后就会失效。
重启后失效的修改(不过我在CentOS 6.5下测试,重启后未发现失效):编辑 /etc/security/limits.conf 文件, 修改后内容为
* soft nofile 1000000
* hard nofile 1000000
永久修改:编辑/etc/rc.local,在其后添加以下内容
ulimit -SHn 1000000
全局限制
执行 cat /proc/sys/fs/file-nr 输出 9344 0 592026
,分别为:1.已经分配的文件句柄数,2.已经分配但没有使用的文件句柄数,3.最大文件句柄数。但在kernel 2.6版本中第二项的值总为0,这并非一个错误,它实际上意味着已经分配的文件描述符无一浪费的都已经被使用了 。
咱们能够把这个数值改大些,用 root 权限修改 /etc/sysctl.conf 文件:
fs.file-max = 1000000
net.ipv4.ip_conntrack_max = 1000000
net.ipv4.netfilter.ip_conntrack_max = 1000000
操做系统上端口号1024如下是系统保留的,从1024-65535是用户使用的。因为每一个TCP链接都要占一个端口号,因此咱们最多能够有60000多个并发链接。我想有这种错误思路朋友不在少数吧?(其中我过去就一直这么认为)
如何标识一个TCP链接:系统用一个4四元组来惟一标识一个TCP链接:{local ip, local port,remote ip,remote port}。好吧,咱们拿出《UNIX网络编程:卷一》第四章中对accept的讲解来看看概念性的东西,第二个参数cliaddr表明了客户端的ip地址和端口号。而咱们做为服务端实际只使用了bind时这一个端口,说明端口号65535并非并发量的限制。
server最大tcp链接数:server一般固定在某个本地端口上监听,等待client的链接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的状况下,即便server端有多个ip,本地监听端口也是独占的,所以server端tcp链接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,所以最大tcp链接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp链接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp链接数约为2的48次方。
TCP/IP 协议规定的,只用了2个字节表示端口号。容易让人误解为1个server只容许链接65535个Client。
typedef struct _NETWORK_ADDRESS_IP
{ USHORT sin_port;//0~65535
ULONG in_addr;
UCHAR sin_zero[8];
} NETWORK_ADDRESS_IP, *PNETWORK_ADDRESS_IP;
(1)其实65535这个数字,只是决定了服务器端最多能够拥有65535个Bind的Socket。也就是说,最多能够开65535个服务器进程,可是你要知道这个可以链接客户端的数量没有任何关系,Accept过来的Socket是不须要Bind任何IP地址的,也没有端口占用这一说。做为Server端的Socket自己只负责监听和接受链接操做。
(2)TCP协议里面是用[源IP+源Port+目的IP+目的 Port]来区别两个不一样链接,因此连入和连出是两个不一样的概念。连出Connect就不错了,须要生成随机端口,这个是有限的连入的话, 因SOCKET的分配受内存分页限制,而链接受限制(WINDOWS)。
(3)因此,千万不要误觉得1个server只容许链接65535个Client。记住,TCP连出受端口限制,连入仅受内存限制。
例如server,IP:192.168.16.254,Port:8009
Client1:IP:192.168.16.1,Port:2378
Client2:IP:192.168.16.2,Port:2378
Client1和Client2虽然Port相同,可是IP不一样,因此是不一样的链接。
(4)想让1个server并发高效得链接几万个Client,须要使用IOCP“完成端口(Completion Port)”的技术。
上面给出的结论都是理论上的单机TCP并发链接数,实际上单机并发链接数确定要受硬件资源(内存)、网络资源(带宽)的限制。
这种单台机器10w并发,不考虑内存cpu的实现,主要是程序网络模型的选择。项目在Github上有提供https://github.com/yaocoder/HPNetServer
linux系统优化设置调整参见:https://www.cnblogs.com/duanxz/p/4464178.html
3.too many open files(打开的文件过多)解决方法
Netty做为通信服务器,发生了打开的文件过多异常,以后在有新的链接就没法链接上来,致使程序崩溃。首先要检查程序断开后是否释放资源,确认无误后在调整文件句柄限制。
too many open files(打开的文件过多)是Linux系统中常见的错误,从字面意思上看就是说程序打开的文件数过多,不过这里的files不单是文件的意思,也包括打开的通信连接(好比socket),正在监听的端口等等,因此有时候也能够叫作句柄(handle),这个错误一般也能够叫作句柄数超出系统限制。
引发的缘由就是进程在某个时刻打开了超过系统限制的文件数量以及通信连接数,经过命令ulimit -a能够查看当前系统设置的最大句柄数是多少:
[dsuser@test02-ds-gps01 ~]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15220
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
open files那一行就表明系统目前容许单个进程打开的最大句柄数,这里是1024。
[dsuser@test02-ds-gps01 ~]$ jps -l
9314 com.hns.gps.gw.jt808.app.Main
17135 sun.tools.jps.Jps
使用命令lsof -p 进程id能够查看单个进程全部打开的文件详情,使用命令lsof -p 进程id | wc -l能够统计进程打开了多少文件:
[dsuser@test02-ds-gps01 ~]$ lsof -p 9314 | wc -l
226
以目前的通信程序为例,能够看到它目前打开了226个文件数,若是文件数过多使用lsof -p 进程id命令没法彻底查看的话,可使用lsof -p 进程id > openfiles.log将执行结果内容输出到日志文件中查看。
解决方法:
一、增大容许打开的文件数——命令方式
ulimit -n 2048
这样就能够把当前用户的最大容许打开文件数量设置为2048了,但这种设置方法在重启后会还原为默认值。
ulimit -n命令非root用户只能设置到4096。
想要设置到8192须要sudo权限或者root用户。
二、增大容许打开的文件数——修改系统配置文件
vim /etc/security/limits.conf
#在最后加入
* soft nofile 4096
* hard nofile 4096
或者只加入
* - hard nofile 8192
最前的 * 表示全部用户,可根据须要设置某一用户,例如
dsuser soft nofile 8192
dsuser hard nofile 8192
注意”nofile”项有两个可能的限制措施。就是项下的hard和soft。 要使修改过得最大打开文件数生效,必须对这两种限制进行设定。 若是使用”-“字符设定, 则hard和soft设定会同时被设定。
三、检查程序问题
若是你对你的程序有必定的解的话,应该对程序打开文件数(连接数)上限有必定的估算,若是感受数字异常,请使用第一步的lsof -p 进程id > openfiles.log命令,得到当前占用句柄的所有详情进行分析,
1)打开的这些文件是否是都是必要的?
2)定位到打开这些文件的代码
3)是否程序操做了文件写入,可是没有进行正常关闭
4)是否程序进行了通信,可是没有正常关闭(也就是没有超时结束的机制)
若是程序中存在这些问题的话,不管系统句柄数设置的多么大,随着时间的推移,也必定会占用完。
总结完毕!延伸阅读:Netty系列之Netty百万级推送服务设计要点 (https://www.cnblogs.com/ruixueyan/p/6382770.html)