socket跟TCP/IP 的关系,单台服务器上的并发TCP链接数问题

常识一:文件句柄限制

Linux下编写网络服务器程序的朋友确定都知道每个tcp链接都要占一个文件描述符,一旦这个文件描述符使用完了,新的链接到来返回给咱们的错误是“Socket/File:Can'topen so many files”linux

这时你须要明白操做系统对能够打开的最大文件数的限制。程序员

  • 进程限制面试

    • 执行ulimit -n 输出1024,说明对于一个进程而言最多只能打开1024个文件,因此你要采用此默认配置最多也就能够并发上千个TCP链接。编程

    • 临时修改:ulimit -n1000000,可是这种临时修改只对当前登陆用户目前的使用环境有效,系统重启或用户退出后就会失效。centos

    • 永久修改:编辑/etc/security/limits.conf 文件, 修改后内容为服务器

      * soft nofile 1000000网络

      * hard nofile 1000000多线程

  • 全局限制并发

    • 执行 cat /proc/sys/fs/file-nr 输出11968 0 1618287,分别为:1.已经分配的文件句柄数,2.已经分配但没有使用的文件句柄数,3.最大文件句柄数。但在kernel2.6版本中第二项的值总为0,这并非一个错误,它实际上意味着已经分配的文件描述符无一浪费的都已经被使用了 。socket

    • 咱们能够把这个数值改大些,用 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,remoteip,remoteport}。好吧,咱们拿出《UNIX网络编程:卷一》第四章中对accept的讲解来看看概念性的东西,第二个参数cliaddr表明了客户端的ip地址和端口号。而咱们做为服务端实际只使用了bind时这一个端口,说明端口号65535并非并发量的限制。

  • server最大tcp链接数:server一般固定在某个本地端口上监听,等待client的链接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的状况下,即便server端有多个ip,本地监听端口也是独占的,所以server端tcp链接4元组中只有remoteip(也就是client ip)和remoteport(客户端port)是可变的,所以最大tcp链接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp链接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp链接数约为2的48次方。


要写网络程序就必须用Socket,这是程序员都知道的。并且,面试的时候,咱们也会问对方会不会Socket编程?通常来讲,不少人都会说,Socket编程基本就是listen,accept以及send,write等几个基本的操做。是的,就跟常见的文件操做同样,只要写过就必定知道。
对于网络编程,咱们也言必称TCP/IP,彷佛其它网络协议已经不存在了。对于TCP/IP,咱们还知道TCP和UDP,前者能够保证数据的正确和可靠性,后者则容许数据丢失。最后,咱们还知道,在创建链接前,必须知道对方的IP地址和端口号。除此,普通的程序员就不会知道太多了,不少时候这些知识已经够用了。最多,写服务程序的时候,会使用多线程来处理并发访问。
咱们还知道以下几个事实:
1。一个指定的端口号不能被多个程序共用。好比,若是IIS占用了80端口,那么Apache就不能也用80端口了。
2。不少防火墙只容许特定目标端口的数据包经过。
3。服务程序在listen某个端口并accept某个链接请求后,会生成一个新的socket来对该请求进行处理。
因而,一个困惑了我好久的问题就产生了。若是一个socket建立后并与80端口绑定后,是否就意味着该socket占用了80端口呢?若是是这样的,那么当其accept一个请求后,生成的新的socket到底使用的是什么端口呢(我一直觉得系统会默认给其分配一个空闲的端口号)?若是是一个空闲的端口,那必定不是80端口了,因而之后的TCP数据包的目标端口就不是80了--防火墙必定会组织其经过的!实际上,咱们能够看到,防火墙并无阻止这样的链接,并且这是最多见的链接请求和处理方式。个人不解就是,为何防火墙没有阻止这样的链接?它是如何断定那条链接是由于connet80端口而生成的?是否是TCP数据包里有什么特别的标志?或者防火墙记住了什么东西?
后来,我又仔细研读了TCP/IP的协议栈的原理,对不少概念有了更深入的认识。好比,在TCP和UDP同属于传输层,共同架设在IP层(网络层)之上。而IP层主要负责的是在节点之间(End 

to End)的数据包传送,这里的节点是一台网络设备,好比计算机。由于IP层只负责把数据送到节点,而不能区分上面的不一样应用,因此TCP和UDP协议在其基础上加入了端口的信息,端口因而标识的是一个节点上的一个应用。除了增长端口信息,UPD协议基本就没有对IP层的数据进行任何的处理了。而TCP协议还加入了更加复杂的传输控制,好比滑动的数据发送窗口(Slice Window),以及接收确认和重发机制,以达到数据的可靠传送。无论应用层看到的是怎样一个稳定的TCP数据流,下面传送的都是一个个的IP数据包,须要由TCP协议来进行数据重组。
因此,我有理由怀疑,防火墙并无足够的信息判断TCP数据包的更多信息,除了IP地址和端口号。并且,咱们也看到,所谓的端口,是为了区分不一样的应用的,以在不一样的IP包来到的时候可以正确转发。
TCP/IP只是一个协议栈,就像操做系统的运行机制同样,必需要具体实现,同时还要提供对外的操做接口。就像操做系统会提供标准的编程接口,好比Win32编程接口同样,TCP/IP也必须对外提供编程接口,这就是Socket编程接口--原来是这么回事啊!
在Socket编程接口里,设计者提出了一个很重要的概念,那就是socket。这个socket跟文件句柄很类似,实际上在BSD系统里就是跟文件句柄同样存放在同样的进程句柄表里。这个socket实际上是一个序号,表示其在句柄表中的位置。这一点,咱们已经见过不少了,好比文件句柄,窗口句柄等等。这些句柄,实际上是表明了系统中的某些特定的对象,用于在各类函数中做为参数传入,以对特定的对象进行操做--这实际上是C语言的问题,在C++语言里,这个句柄其实就是this指针,实际就是对象指针啦。
如今咱们知道,socket跟TCP/IP并无必然的联系。Socket编程接口在设计的时候,就但愿也能适应其余的网络协议。因此,socket的出现只是能够更方便的使用TCP/IP协议栈而已,其对TCP/IP进行了抽象,造成了几个最基本的函数接口。好比create,listen,accept,connect,read和write等等。
如今咱们明白,若是一个程序建立了一个socket,并让其监听80端口,实际上是向TCP/IP协议栈声明了其对80端口的占有。之后,全部目标是80端口的TCP数据包都会转发给该程序(这里的程序,由于使用的是Socket编程接口,因此首先由Socket层来处理)。所谓accept函数,其实抽象的是TCP的链接创建过程。accept函数返回的新socket其实指代的是本次建立的链接,而一个链接是包括两部分信息的,一个是源IP和源端口,另外一个是宿IP和宿端口。因此,accept能够产生多个不一样的socket,而这些socket里包含的宿IP和宿端口是不变的,变化的只是源IP和源端口。这样的话,这些socket宿端口就能够都是80,而Socket层仍是能根据源/宿对来准确地分辨出IP包和socket的归属关系,从而完成对TCP/IP协议的操做封装!而同时,放火墙的对IP包的处理规则也是清晰明了,不存在前面设想的种种复杂的情形。
明白socket只是对TCP/IP协议栈操做的抽象,而不是简单的映射关系,这很重要.

 

关于TCP服务器最大并发链接数有一种误解就是“由于端口号上限为65535,因此TCP服务器理论上的可承载的最大并发链接数也是65535”。

先说结论:对于TCP服务端进程来讲,他能够同时链接的客户端数量并不受限于可用端口号。并发链接数受限于linux可打开文件数,这个数是能够配置的,能够很是大,因此实际上受限于系统性能。

从理论上说,端口号的做用是在网络链接中标识应用层的进程,服务端通常使用众所周知的端口号进行监听,客户端链接时系统自动分配端口号。一个服务端进程服务于n个客户远程进程,只须要能经过ip地址+端口号的组合把他们区分开便可,没有必要占用本机的其余端口号,客户端链接数增长并不会占用服务器端口号,所以端口号并不能限制并发链接数。固然一台机器上端口号数量的上限确实是65536个,由于tcp首部中使用16bit去存储端口号。因此若是说65536影响了链接数,只有一种可能,就是同一台客户端机子上开n个进程去连同一个服务端进程,由于客户端ip是同一个,为了区分出这些链接,只能使用客户端链接的端口号,那么服务端和一个客户端主机之间的tcp链接数理论上线确实是65536。可是,服务端能够链接n多客户端机子呢。
实际上,确实有个限制端口号的配置,就是MaxUserPort,这其实是一台主机向外链接使用端口数的限制,这个数也能够配置的,可能默认值才5000,实际上对于正常的服务器主机是够用的,由于你是等别人链接进来的,不是要去链接不少不一样的其余主机的。固然你的服务器上可能跑了一些转发的服务,这样你就须要对外链接了,若是被限制在这个配置这儿了确实须要改。可是这个MaxUserPort确实和服务器能够承载的来自客户端的并发链接数没有关系。

伴随这个误解的还有另一个误解,就是accept以后产生的已链接套接字占用了新的端口。这个绝对是错误的,linux内核不会这么写的,由于彻底不必嘛。客户端链接上来以后产生的这个socket fd就是用来区分客户端的,里面会填上客户端的ip和端口做为发包用,来自客户端的包也会使用这个fd去读取。能够试试netstat -ano,而后起一个服务器看下,客户端连上来这后产生的套接字的服务端端口仍是监听的端口。

高并发网络链接数因端口数量受限问题

遇到的问题:端口数量受限

通常来讲,单独对外提供请求的服务不用考虑端口数量问题,监听某一个端口便可。可是向提供代理服务器,就不得不考虑端口数量受限问题了。当前的1M并发链接测试,也须要在客户端突破6万可用端口的限制。

单机端口上限为65536

端口为16进制,那么2的16次方值为65536,在linux系统里面,1024如下端口都是超级管理员用户(如root)才可使用,普通用户只能使用大于1024的端口值。 
系统提供了默认的端口范围:cat /proc/sys/net/ipv4/ip_local_port_range    1024 65535

大概也就是共65535-1024=64511个端口可使用,单个IP对外只能发送64511个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地址,协议号,源端口,目的端口}

在《UNIX网络编程卷1:套接字联网API(第3版)》一书中,是这样解释:

一个TCP链接的套接字对(socket pari)是一个定义该链接的两个端点的四元组,即本地IP地址、本地TCP端口号、外地IP地址、外地TCP端口号。套接字对惟一标识一个网络上的每一个TCP链接。 
...... 
标识每一个端点的两个值(IP地址和端口号)一般称为一个套接字。

 

如下以四元组为准。在测试端四元组能够这样认为:

{本机IP地址,本机端口,目的IP地址,目的端口}

请求的IP地址和目的端口基本上是固定的,不会变化,那么只能从本机IP地址和本机端口上考虑,端口的范围一旦指定了,那么增长IP地址,能够增长对外发出的请求数量。假设系统可使用的端口范围已经如上所设,那么可使用的大体端口为64000个,系统添加了10个IP地址,那么能够对外发出的数量为 64000 * 10 = 640000,数量很可观。

只有{源IP地址,源端口}肯定对外TCP请求数量

经测试,四元组里面,只有{源IP地址,源端口}才可以肯定对外发出请求的数量,跟{目的IP地址,目的端口}无关。

相关文章
相关标签/搜索