如何标识一个TCP连接
对于TCP协议,成功建立一个新的链接,需要保证该TCPl连接中四个要素组合体的唯一性:客户端的IP、客户端的port、服务器端的IP、服务器端的port
。
服务器端的同一个IP和port,可以和同一个客户端的多个不同端口成功建立多个TCP链接(与多个不同的客户端当然也可以),只要保证Server IP + Server Port + Client IP + Client Port
这个组合唯一不重复即可。
如果是客户端用同一个本地端口去连不同的两个服务器ip,连第二个时就会提示端口已被占用。但服务器的监听端口,可以accept多次,建立多个socket,connection。 当error为EAGAIN或者EWOULDBLOCK时代表套接字被标记为非阻塞,且当前没有可接收的连接。
套接字与TCP连接
监听套接字就是个牵线指路的,你实质上是跟它指的那个人说话。因为你要找的那个人不可能随时等你来,而监听套接字就是专职等你来问,它回答你要找的人在哪,并唤醒你要找的人,于是通话就建立起来了,就像现实生活中的接线员一样。
listen函数将一个套接字转化为监听套接字。监听套接字可以接受来自客户端的连接请求。服务器通过accept函数等待来自客户端的连接请求,请求先到达监听套接字 listenfd,并返回一个已连接套接字。利用 I/O 函数,这个 已连接套接字可以被用来与客户端进行通信。监听套接字,是服务器作为客户端连接请求的一个端点,它被创建一次,并存在于服务器的整个生命周期。已连接套接字是客户端与服务器之间已经建立起来了的连接的一个端点,服务器每次接受连接请求时都会创建一次已连接套接字,它只存在于服务器为一个客户端服务的过程中。无论是监听套接字,还是已连接套接字,都是只存在于服务器端。已连接套接字与监听套接字共用端口号。
在IO连接建立后,客户端用发出连接的那个SOCKET向服务器发数据,是发给服务器新创建的已连接套接字SOCKET,而不是服务器的监听SOCKET。服务器的监听SOCKET永远只是用来接受连接请求。操作系统上端口号1024以下是系统保留的,从1024-65535是用户使用的。由于每个TCP连接都要占一个端口号,所以错误认为最多可以有60000多个并发连接。其实一个网卡对应一个IP地址,一个IP地址对应65535个端口,一个socket(addr, port)可以接受多个socket连接(accept)。一个端口只能被一个socket监听(listen)。最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制。这是因为系统为每个TCP连接被accept后都要创建一个连接套接字socket句柄,每个socket句柄同时也是一个文件句柄。
服务端的最大连接数
在上述TCP连接中讲解了Server IP + Server Port + Client IP + Client Port
这个组合标识一个连接。服务端通常固定在某个本地端口上监听,等待客户端的连接请求。如果不考虑地址重用(SO_REUSEADDR选项)的情况下,即使服务端端有多个网卡ip,本地监听端口也是独占的,因此服务端TCP连接四元组中只有Client IP + Client Port
是可变的,因此服务端TCP最大连接为Client IP 数×Client Port数,对IPV4,不考虑ip地址分类等因素,最大TCP连接数约为2的32次方(ip数)×2的16次方(port数),也就是服务端端单机理论最大TCP连接数约为2的48次方。但是如上所述最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制。这是因为系统为每个TCP连接被accept后都要创建一个连接套接字socket句柄,每个socket句柄同时也是一个文件句柄。
修改服务端TCP的最大连接数
ulimit -n 输出 的结果,说明对于一个进程而言最多能打开多少个文件,所以你要采用此默认配置最多也就可以并发上千个TCP连接。临时修改:ulimit -n 1000000,但是这种临时修改只对当前登录用户目前的使用环境有效,系统重启或用户退出后就会失效。永久修改:编辑/etc/rc.local,在其后添加如下内容:ulimit -SHn 1000000
如果一个程序创建了一个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协议的操作封装。已连接套接字与监听套接字共用端口号。