socket accept()返回的socket描述符的端口和listen描述符端口是一样的吗?
as you know,一个socket是由一个五元组来唯一标示的,即(协议,server_ip, server_port, client_ip, client_port)。只要该五元组中任何一个值不同,则其代表的socket就不同。这里忽略协议的区别,在同一协议的基础上,服务器端的listen socket的端口可以看成(server_ip, server_port, ***, ***),其中***是通配符,它跟任何一个client_ip, client_port值都不同,可以简单看成是(0,0)对,当然实现不是这样的。这样在服务器端accept之后,返回的连接socket的四元组就是(server_ip, server_port, client_ip, client_port),这里的client_ip,client_port因连接的客户端的不同而不同。所以accept返回的socket和listen socket是不同的,不同之处就在于四元组中的客户端ip和port,而服务器端的server_ip和server_port还是相同的,也就是accpet()函数返回的新的socket描述符的端口和listen端口是一样的。可以使用getsockname()函数来查看它们之间的不同。
http://blog.csdn.NET/hanzengyi/article/details/5365029
accept是又产生一个Socket端口吗?
要写网络程序就必须用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协议栈操作的抽象,而不是简单的映射关系,这很重要!
http://blog.csdn.Net/zztfj/article/details/10103621http://blog.csdn.net/zztfj/article/details/10103621
单机最大的TCP连接数及其修改
一个误解: 单个服务器程序可承受最大连接数“理论”上是“65535” .
65535这个数字的由来,很多人想当然地将它与port最大值联系起来。的确,TCP的端口数,最大值确实为65535。但是,这并不代表一个服务器可以接受的连接数就是这个值。很多人之所以把这两个概念搞混淆是因为对socket和port没有更深的认识和理解。我们先来回想一下服务器服务的先后过程:
1、服务器创建监听socket
2、与对外服务的端口号绑定
3、开始listen
4、客户端连接到服务器对应的port
5、服务器accept为新的客户端产生新的socket
6、基于这个新的socket与客户端交换数据。
从以上流程来看,最大值为65535的“端口号”这个重要的东东,我们只用了一次,就是执行bind的时候!而以后创建的socket,说白了就是一个可以进行网络IO操作的HANDLE而已。通过查看该HANDLE的RemoteEndPoint能查看到远程客户端连接的IP和端口号(注意,该端口是远程客户端的端口),查看该HANDLE的LocalEndPoint能看到该Socket的Ip和端口就是该服务绑定的IP和端口。所以,accept的socket值与端口号无关,又何来65535的“理论”上限?
好了,已经弄明白了服务器端接收的客户端连接连接数不受最大端口号65535的限制。但是,在客户端,应用程序最多可以建立多少个TCP连接呢?以及如何调整系统参数来调整单机的最大TCP连接数。
Windows 下单机的TCP连接数有多个参数共同决定,下面一一介绍:
最大TCP连接数
[HKEY_LOCAL_MACHINE System CurrentControlSet Services Tcpip Parameters]
TcpNumConnections = 0x00fffffe (Default = 16,777,214)
以上注册表信息配置单机的最大允许的TCP连接数,默认为 16M。这个数值看似很大,这个并不是限制最大连接数的唯一条件,还有其他条件会限制到TCP 连接的最大连接数。
最大动态端口数
TCP客户端和服务器连接时,客户端必须分配一个动态端口,默认情况下这个动态端口的分配范围为 1024-5000 ,也就是说默认情况下,客户端最多可以同时发起3977 个Socket 连接。我们可以修改如下注册表来调整这个动态端口的范围
[HKEY_LOCAL_MACHINE System CurrentControlSet Services Tcpip Parameters]
MaxUserPort = 5000 (Default = 5000, Max = 65534)
最大TCB 数量
系统为每个TCP 连接分配一个TCP 控制块(TCP control block or TCB),这个控制块用于缓存TCP连接的一些参数,每个TCB需要分配 0.5 KB的pagepool 和 0.5KB 的Non-pagepool,也就说,每个TCP连接会占用 1KB 的系统内存。
系统的最大TCB数量由如下注册表设置决定
[HKEY_LOCAL_MACHINE System CurrentControlSet Services Tcpip Parameters]
MaxFreeTcbs = 2000 (Default = RAM dependent, but usual Pro = 1000, Srv=2000)
非Server版本,MaxFreeTcbs 的默认值为1000 (64M 以上物理内存)
Server 版本,这个的默认值为 2000。
也就是说,默认情况下,Server 版本最多同时可以建立并保持2000个TCP 连接。
最大TCB Hash table 数量
TCB 是通过Hash table 来管理的,下面注册表设置决定了这个Hash table 的大小
HKEY_LOCAL_MACHINE System CurrentControlSet services Tcpip Parameters]
MaxHashTableSize = 512 (Default = 512, Range = 64-65536)
这个值指明分配 pagepool 内存的数量,也就是说,如果MaxFreeTcbs = 1000 , 则 pagepool 的内存数量为 500KB
那么 MaxHashTableSize 应大于 500 才行。这个数量越大,则Hash table 的冗余度就越高,每次分配和查找 TCP 连接用时就越少。这个值必须是2的幂,且最大为65536.
IBM WebSphere Voice Server 在windows server 2003 下的典型配置
这是IBM WebSphere Voice Server 的典型配置,大家可以做个参考。原文参见
IBM Web Sphere Voice Server 配置
•MaxUserPort = 65534 (Decimal)
•MaxHashTableSize = 65536 (Decimal)
•MaxFreeTcbs = 16000 (Decimal)
这里我们可以看到 MaxHashTableSize 被配置为比MaxFreeTcbs 大4倍,这样可以大大增加TCP建立的速度。
http://www.zhihu.com/question/30772664
单个连接消耗内存的地方.
- /proc/sys/net/ipv4/tcp_rmem (for read)
- /proc/sys/net/ipv4/tcp_wmem (for write)
然后是逻辑IO缓冲区
就是比如你监听了recv事件 事件来了 你要有内存可用(一般都是socket建立起就分配好,断开才会释放的).
这个内存是自己写socket程序时候自己控制的, 最低也要4K,4K, 实际使用8K,8K至少.
现在设定一个优化方案和使用场景, 首先假设4G内存全部为空闲(系统和其他进程也要内存的....
假如网络包的大小都可以控制在4K以下, 假设所有连接的网络都不会拥堵, 或者拥堵时候的总量在4K以下:
一个连接的内存消耗是4+4+4+4=16K
4G/16K=26.2万并发
假如网络包的大小都可以控制在8K以下, 假设所有连接的网络都不会拥堵, 或者拥堵时候的总量在8K以下
一个socket的内存占用介于 24K ~ 32K之间, 保守的按照32K算
4G/32K=13.1万并发, 这个在生产环境作为一个纯网络层面的内存消耗, 是可以作为参考的.
假如使用默认配置, 假如所有连接的网络都出现严重拥堵, 不考虑逻辑上的发送队列的占用,
使用默认配置是2M+2M+8+8 ~= 4M
4G/4M=1024并发 ( ...
如果考虑到发送队列也拥堵的话 自己脑补.
如果只是为了跑分 为了并发而优化, 没有常驻的逻辑缓冲区 并且socket的网络吞吐量很小并且负载平滑, 把socket buffer size设置系统最低.
那么是
4G/8K = 52.4万并发 这个应该是极限值了.