假如客户端连接的是服务端的80端口,所有的数据都发送给80端口,这个时候,tcp协议中的端口是80。但是服务端只是监听80端口,然后使用一个新的socket和客户端通信,新的socket端口是重新分配的,假设端口号分配的是12345,服务端将响应数据从这个端口发送出去,长连接建立起来之后,服务端从新的12345端口接受数据,但是客户端还是将数据发送到80端口(因为客户端创建套接字的时候,只知道服务端公开的端口是80),那么服务端是怎么在新的12345端口上获取数据的?换句话说,客户端tcp协议的目的端口是80,12345端口如何收到发送到80端口的数据?
这个问题的产生是基于一个错误的概念,所以接下来的一切推理都是错误的。
就如同问,小明有两个脑袋,别人和小明点头致意时,小明应该点哪个脑袋?
正常人只有一个脑袋,还有其它选择吗?如果你非要说,小明是连体婴儿,有两个脑袋!
那也只能说,另外一个脑袋是大明的,并不是小明的!
服务器在端口=80监听,会一直使用端口80服务来自本机客户端、本局域网客户端、互联网客户端的任何连接请求,而不会使用任何其它端口!
服务器的处理流程又是怎样的呢?概括起来有三个处理分支:
1.外部报文到达时,TCP连接不存在,连接合法
2.外部报文到达时,TCP连接已存在
3.外部报文到达时,TCP连接不存在,连接不合法
分支1:外部报文到达时,TCP连接不存在,连接合法
新建一个socket,socket可以理解为插座+ 插头,谁是插座呢?
“服务器的IP+ 端口号(80)”这一固定组合可以看做插座。
谁又是插头呢?
“客户端的IP+ 端口号”这一临时搭档可以看做插头。
话说服务器这个插座不就是给客户端插入的吗?一旦插头插入插座,就形成了一个端到端的虚拟连接。客户端与服务器接下来的通信就在这个虚拟连接上穿梭。
服务器的客户遍天下,高峰连接时间甚至有上千万的客户端(插头)来连接服务器(插入插座),那么意味着会有上千万个虚拟连接(插座 + 插头,黄金搭档)。那服务器怎么快速找到这个虚拟连接管线呢?
ID =(源IP,目的IP,源端口,目的端口)
通过这四个元素的组合,可以唯一识别这个虚拟管线?
可以是可以,但是这种查表方式效率太低!
如果将四个元素运行一个简单的hash函数,那么会生成一个全局唯一的ID值,用这个ID当做关键字来查虚拟连接表,效率会提高许多。
ID = Hash(源IP,目的IP,源端口,目的端口)
此外,为了最大化提高查找效率,通常在组织虚拟连接表时,以ID为主键,建立几级索引,和查字典一个原理。
一旦插头插入插座,属于这个连接的下一个报文还会进入分支1吗?
当然不会,会进入分支2!
分支2:外部报文到达时,TCP连接已存在
TCP根据接收报文的四元素,计算ID,以ID值为关键字在TCP连接表里查找,能够进入分支2处理流程的,一定是查找到的ID。接下来的处理流程,就是正常的TCP协议处理流程,缓存数据、更新状态、发送确认消息等等。
如果没有查找到ID,且接收报文并没有携带SYN标志位的,很抱歉,这是一个非法报文。进入分支3处理流程。
分支3:外部报文到达时,TCP连接不存在,连接不合法
没有建立连接就发数据,就如同没有恋爱就上床,是一种流氓行为,必须严厉打击!怎么打击呢?
发Reset报文予以拒绝!
如果流氓无赖是故意攻击服务器,目的就是为了消耗服务器发报文而产生的CPU开销,一秒钟发1000000+次分支3的非法报文,服务器也要发1000000+次Reset报文吗?
服务器对于这些异常情况,服务器只要预设处理上限值,达到一定的上限,只丢包而不回包。
服务器前端的流量监控设备,也会监控到这种异常情况,予以流量清洗。
话又说回来,流氓无赖一般还是会选择发SYN来攻击服务器,不仅消耗服务器的CPU,还可以消耗服务器的内存,这就是SYN Flood攻击。
领取专属 10元无门槛券
私享最新 技术干货