白话TCP/IP原理

Posted 编程一生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了白话TCP/IP原理相关的知识,希望对你有一定的参考价值。

NioEventLoopGroupNioEventLoopGroup ServerBootstrap b ServerBootstrap bworkerGroup 100 LoggingHandler ChannelFuture f b f bossGroup workerGroup.shutdownGracefully();

这段代码来自经典的echo服务器,我们在childOption中开启了SO_KEEPALIVE
Java程序只能做到设置SO_KEEPALIVE选项,其他配置项只能依赖于sysctl配置,系统进行读取。

使用的场景

一般我们使用KeepAlive时会修改空闲时长,避免资源浪费,系统内核会为每一个TCP连接建立一个保护记录,相对于应用层面效率更高。

常见的几种使用场景:

  • 检测挂掉的连接(导致连接挂掉的原因很多,如服务停止、网络波动、宕机、应用重启等)

  • 防止因为网络不活动而断连,如使用NAT代理或者防火墙的时候,经常会出现这种问题

  • HTTP协议的Keep-Alive意图在于连接复用,同一个连接上串行方式传递请求-响应数据

  • TCP的KeepAlive机制意图在于保活、心跳,检测连接错误

  • KeepAlive通过定时发送探测包来探测连接的对端是否存活,但通常也会许多在业务层面处理,他们之间的特点是:

  • TCP自带的KeepAlive使用简单,发送的数据包相比应用层心跳检测包更小,仅提供检测连接功能

  • 应用层心跳包不依赖于传输层协议,无论传输层协议是TCP还是UDP都可以用

  • 应用层心跳包可以定制,可以应对更复杂的情况或传输一些额外信息

  • KeepAlive仅代表连接保持着,而心跳包往往还代表客户端可正常工作

  • 像Dubbo这种通信中间件都使用到了TCP的保活机制。k8s客户端和服务端是基于http协议的长连接,用到了http的保活复用连接。

    连接池技术

    上面四次挥手讲了,如果服务器总是在断开连接,tcp会总是处于time wait状态。很多连接没有得到真正的释放。像数据库操作,是非常频繁的。咱们一般都不会像下图这么每次都申请和关闭连接吧。

    所以就希望尽可能的复用三次握手和四次挥手的过程,让客户端和服务端投入更多的资源在数据传输上。这就要用到连接池。

    连接池是一种池式结构。其他的池式结构有:线程池、协程池、内存池和对象池。它们的实现都很接近。连接池中也有很多大家听说过的场景,如:数据库连接池、MQ的连接池、Redis的连接池。

    提到MQ的连接池,之前的时候,有个同事排查我们使用的MQ并发吞吐太低。因为用的是标准的Java消息服务JMS客户端,跟了代码发现里面用到了connection.close。就怀疑用的短连接。其实close方法的实现一般不是销毁连接,还是归还到连接池。

    连接池咱们平时使用最多、面试最多的是其中控制伸缩性的参数:最小连接数、最大连接数。最小连接数主要是要限定池的大小,最大连接数主要是限定能打开的最大连接数。使用连接池进行通信的流程如下图所示:

    关于连接池更详细的内容因为预计还要2千字以上,我会专门写一篇文章来讲。

    思极恐的Socket

    在《接下来一段时间会对大家进行网络通信的魔鬼训练-理解socket》中我专门讲过socket,它功能强大但是让人细思极恐。之前发生过一个线上问题,某个时刻发生了几笔请求超时,排查发现打印Dubbo调用的来源IP时,触发了DNS反解析。网络闪断,连接不到DNS服务器,结果夯住10s直到超时。

    熟悉Java语言的朋友可以了解一下当时线上问题的原因:

    dubbo调用的来源IP使用的是dubbo的

    RpcContext.getContext().getRemoteAddress().getHostName();

    因为RpcContext.getContext()是基于上篇文章《ThreadLocal&MDC内存泄漏问题》中提到的ThreadLocal,就是保存在了线程里的一个固定值。所以也就相当于

    new InetSocketAddress(固定的IP, 固定的端口).getHostName();

    这个方法会进行DNS lookup!所以具体这个函数的执行时间受到网络状况的影响。建议可以直接使用IP的地方使用getAddress()代替getHostName()。

    这个问题可以这么来理解:

    相当于在程序里执行了一个linux命令:nslookup 某IP

    为什么说让人细思极恐呢?想想看,使用Socket编程,只需一行代码,有时候就是隐式的,实际上却可能发出了一个绕地球半圈的信号,关键多数开发者还不知道。

    总结

    如大家所见,本篇算是《白话linux操作系统原理》的姊妹篇。和《网络通信之Session的历史血脉》、《深入理解MQ生产端的底层通信过程-理解channel》《接下来一段时间会对大家进行网络通信的魔鬼训练-理解socket》《网络字节序列-大端序和小端序》《https引起的跨域问题-COE&casestudy》《懂得三境界-使用dubbo时请求超过问题》《一个http请求进来都经过了什么(2021版)》、《架构师之路-https底层原理》是一个系列。

    在之前的文章中说过,我尽量想办法让大家能把这些系列文章有兴趣看下去。如果大家坚持下去,会对以后的工作有很大帮助。《白话linux操作系统原理》这篇文章发出之后,反馈特别好。我就在想是不是这里存在着可以让大家坚持下去的点。可是,我自己都没搞清楚这个点在哪里。

    只要是用心写了,对自己的知识能力也有自信,知道所写的对大家知识能力是很有帮助的。那就坚持下去。相信可以帮助到大家,也为祖国的科技发展尽一份力。

    TCP/IP协议三次握手和四次挥手大白话解说

    TCP/IP协议三次握手和四次挥手大白话解说

    前言

    昨天晚上被一位师傅问到了TCP/IP的工作机制,心里很清楚三次握手,然而对于四次挥手却忘了,这是大学习里学过的,奋而翻阅书籍和网络对之前所学的做一个温顾,算是夯实自我吧。

    TCP(Transmission Control Protocol)网络传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议,数据传输前建立连接的工作要经过三次握手,数据传输后断开连接的工作要经过四次挥手。

    工作过程

    TCP标志位:

    TCP共有6个标志位,分别是:

    • SYN(synchronous),建立联机。
    • ACK(acknowledgement),确认。
    • PSH(push),传输。
    • FIN(finish),结束。
    • RST(reset),重置。
    • URG(urgent),紧急。

    图解三次握手和四次挥手的过程:

    技术分享图片

    三次握手建立连接阐述:

    第一次握手:客户端要和服务端进行通信,首先要告知服务端一声,遂发出一个SYN=1的连接请求信号,”服务端哥哥,我想给你说说话”。

    第二次握手:当服务端接收到客户端的连接请求,此时要给客户端一个确认信息,”我知道了(ACK),我这边已经准备好了,你现在能连吗(SYN)”。

    第三次握手:当客户端收到了服务端的确认连接信息后,要礼貌的告知一下服务端,“好的,咱们开始联通吧(ACK)”。

    到此整个建立连接的过程已经结束,接下来就是双方你一句我一句甚至同时交流传递信息的过程了。

    四次挥手断开连接阐述:

    第一次挥手:双方交流的差不多了,此时客户端也已经结尾了,接下来要断开通信连接,所以告诉服务端“我说完了(FIN)”,此时自身形成等待结束连接的状态。

    第二次挥手:服务端知道客户端已经没话说了,服务端此时还有两句心里话要给客户端说,“我知道你说完了(ACK),我再给你说两句,&*……%¥”。

    第三次挥手:此时客户端洗耳恭听继续处于等待结束的状态,服务器端也说完了,自身此时处于等待关闭连接的状态,并对告诉客户端,“我说完了,咱们断了吧(FIN)”。

    第四次挥手:客户端收知道服务端也说完了,也要告诉服务端一声(ACK),因为连接和断开要双方都按下关闭操作才能断开,客户端同时又为自己定义一个定时器,因为不知道刚才说的这句话能不能准确到达服务端(网络不稳定或者其他因素引起的网络原因),默认时间定为两个通信的最大时间之和,超出这个时间就默认服务器端已经接收到了自己的确认信息,此时客户端就关闭自身连接,服务器端一旦接收到客户端发来的确定通知就立刻关闭服务器端的连接。

    到此为止双方整个通信过程就此终结。这里要声明一下:断开链接不一定就是客户端,谁都可以先发起断开指令,另外客户端和服务端是没有固定标准的,谁先发起请求谁就是客户端。

    问题:

    1.为什么断开链接的时候客户端设置的定时器时间等待要2MSL(两个通信报文的最大时间)? 
    这个问题也很好理解,当客户端最终告诉服务器端断开确认的时候,他不知道自己的发出的指令是否能准确的一次性被服务器接收。假如服务器没有接收到(这已经耗费了一个报文的最大通信时间了),服务器端将会重新发起一个结束通话的指令(FIN)到客户端,客户端又接收到了服务器发来的结束通信指令将继续给服务器进行一个确认,有人会说那要是客户端发出的确认信息服务端没收到,而服务端重发的断开指令客户端也没收到怎么办,说实话我也无奈,遇到这种情况咱们干脆认为网确实不行了。

    2.为什么建立连接要三次握手而断开连接要四次挥手? 
    说起这个,打一个比喻,目前祖国正在高速发展高铁,建立连接的过程正如上海到北京打通一条高铁线,TCP通信过程是一个全双工模式,即在这条高铁线上要有两个轨道,即能从上海发车到北京又能从北京发车到上海,甚至两边可以同时发车。所以断开连接前提就是要保证两条轨道都没有车,然后双方才能各自发起断开动作。

    双方各自工作流程图:

    客户端工作流程: 
    技术分享图片 
    注意:在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。

    服务器端工作流程: 
    技术分享图片

    附加

    SYN网络攻击:

    原理: 
    在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。

    检测: 
    SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了。windows下打开cmd,输入命令:”netstat -n -p TCP“,查看是否有大量的”SYN_RECEIVED“状态。

    以下图片是表明正常的。 
    技术分享图片

    参考:TCP/IP协议三次握手和四次挥手大白话解说

    以上是关于白话TCP/IP原理的主要内容,如果未能解决你的问题,请参考以下文章

    大白话解说TCP/IP协议三次握手和四次挥手

    学习socket.io前的网络基础知识准备(白话讲解)

    白话linux操作系统原理

    TCP / IP协议 --- 用户层面

    计算机底层原理杂谈(白话文)

    白话HTTPS