面试官求你了,别再问我TCP的三次握手和四次挥手

少点代码,多点头发

本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。

https://github.com/midou-tech/articles

三次握手建立链接,四次挥手断开链接。这个问题算非常经典的问题,也是面试官非常喜欢问的问题。

不夸张的说,龙叔在校招面试的时候每一家公司都问到过关于三次握手和四次挥手相关的问题,相信大家也都差不多被面试官各种怼。

这个问题的重要性,已经意识到。不说废话了,接下来就是听龙叔给你安排的明明白白。

先画个图,看下TCP的建立连接 和 断开连接的整体过程。

tcp三次握手四次挥手

看完这个图相信聪明的你在整体对三次握手和四次挥手有了一些基本把控。但是,里面的细节肯定是会有些生疏或者模糊的,接下来就一个一个问题的揭露本质。

在解释之前先看点基础知识做做铺垫。

TCP状态转移解释

状态 描述
CLOSED 阻塞或关闭状态,表示主机当前没有正在传输或者建立的链接
LISTEN 监听状态,表示服务器做好准备,等待建立传输链接
SYN RECV 收到第一次的传输请求,还未进行确认
SYN SENT 发送完第一个SYN报文,等待收到确认
ESTABLISHED 链接正常建立之后进入数据传输阶段
FIN WAIT1 主动发送第一个FIN报文之后进入该状态
FIN WAIT2 已经收到第一个FIN的确认信号,等待对方发送关闭请求
TIMED WAIT 完成双向链接关闭,等待分组消失
CLOSING 双方同时关闭请求,等待对方确认时
CLOSE WAIT 收到对方的关闭请求并进行确认进入该状态
LAST ACK 等待最后一次确认关闭的报文

再看下TCP的报文格式

TCP报文格式

首部有20字节的固定长度,含义如下:

  1. 源端口和目的端口

各占2字节,就是存储源端口号和目的端口的

  1. 序号seq

占4字节,表示的范围就是整形的范围[0~2^32]。序号使用在给数据部分每个字节进行编号的,编号方式是mod 2^32 。

  1. 确认号ack

占4字节,范围也是无符号整数的范围。使用在对端传输给我的数据最后一个字节序号,例如A传输给B 101—500,此时B返回的确认号一定是小于等于501的。当B段正确接收数据之后才会返回确认号,换句话说确认号之前的数据已经全部接收。

  1. 数据偏移

占4bit,数据偏移很多人很容易想到是不是表示数据的长度,那就错了。偏移嘛,指的是TCP起始位置到数据部分的起始位置的偏移,也就是TCP首部的长度。

  1. 保留

占6bit,保留字段顾名思义,就是为今后使用,默认置为0。

  1. 紧急URG控制位

占用1bit,URG=1,表示紧急指针有效,此时tcp数据优先传输。相当于生活中的紧急通道,特殊情况时使用。

在网络中也会有特殊情况,例如,发送一个很长的程序在远程服务器上运行,此时发现程序有bug,需要中断运行,因此我们从键盘输入Ctrl c,假如不使用紧急数据,需要在缓冲区里排队,都知道是bug了,还要排队,这怕是要出锅啊。

此时使用紧急数据传输,不需要排队,直接中断程序是不是更符合我们的预期。

需要注意一点是,即使窗口为0时,也可以发送紧急数据。

如何使用紧急URG控制位,在socket编程中send函数flag参数

send(int socket, const void *buffer, size_t length, int flags);

flags参数传MSG_OOB宏时,表示此时有紧急数据。MSG_OOB是个宏,

  1. 确认ACK

占1bit,当ACK=1时生效。TCP有条硬性规定,当建立链接成功后所有传输的数据报文都必须把ACK置为1。

  1. 推送PSH

占1bit,发送方把PSH置为1时 会立即发送该数据包,接收方收到PSH=1的报文会立即处理交付给应用层处理。是不是感觉和URG很像,其实还是有些区别的。

  • 两者相同点:

URG与PSH两者都使用于紧急处理的情况,用来快速传输紧急数据。

  • 两者不同点

URG置为1时,对于发送发,“带外数据”与正常情况下应该发送的消息数据一起,封装成数据报发送,省去了在队列中等待的时间。
在接收方,解析报文后,获取数据之后还是要放在缓存区中,等待满了之后在向上往应用层交付。

PSH置为1时,对于发送方,表明这些数据不需要等向下发送的缓存区满,立刻封装成报文,发送,省去了等待发送缓存区到达满的状态的时间。
在接收方,也不需要等接受缓存区满,直接向上交付给应用层。

  1. 复位RST

占1bit,当RST=1时,TCP会主动释放链接,两种情况会用上。

TCP出现严重差错时,会主动释放连接,重建链接,传输数据。

遇到非法报文或者拒绝连接时会把RST置为1.

  1. 同步SYN

占1bit,同步控制位,用来在传输连接建立时同步传输连接序号。

SYN=1时,表示这是一个连接请求或连接确认报文。

SYN=1,ACK=0,表明这是一个连接请求数据段,如果对方同意建立连接,则对方会返回一个SYN=1、ACK=1的确认。

  1. FIN控制位

占1bit,用于释放一个传输连接。

FIN=1时,表示数据已全部传输完成,发送端没有数据要传输了,要求释放当前连接,但是接收端仍然可以继续接收还没有接收完的数据。

FIN=0,正常传输数据。

  1. 窗口大小

占16bit,2byte,用于表示发送方可以接受的最大数据大小。

该窗口是动态变化的,用作流量控制时使用。

  1. 检验和

占16bit,2byte,用于对TCP头部,伪头部,数据三个部分进行校验。

  1. 紧急指针

占16bit,2byte,用于记录紧急数据的末尾在数据段中的位置

当URG=1时,该指针才生效。

  1. 可选项

可选项最长可达40byte,是可选的,可以没有。当可选项不存在时,TCP头部长度为20byte。

可选项可以包括窗口缩放选项(Window ScaleOption,WSopt)、MSS(最大数据段大小)选项、SACK(选择性确认)选项、时间戳(Timestamp)选项等。

  1. 数据

TCP数据部分,由应用层应用程序提交的数据。

TCP头部是基础知识,必须了解才能更好的理解TCP数据如何封装和传输,以及在建立链接和断开链接时都在操作那些地方。

三次握手建立连接

三次握手如何建立连接?

三次握手建立链接

从图中可以清楚的看到,三次握手的过程,我在在把过程清楚的解释一遍,顺便说下每个过程容易被问到的知识点。

采用C/S模式解释,假设C端发起传输请求。

在发送建立链接请求之前,C端是保持CLOSED状态,S端最开始也是处于CLOSED状态,当执行listen函数套接字进入被动监听状态

所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

第一次:C端发送SYN=1的请求报文,此时C端进入SYN SENT状态,等待服务器确认。

此时如果报文丢失发送不到对端会如何?

C端发送报文之后会启动一个定时器,在超时之后未收到S端的确认,会再次发送SYN请求,每次尝试的时间会是第一次的二倍,如果总的总尝试时间为75秒,此次建立链接失败。

第二次:S端收到C端发送的SYN报文(建立链接请求)后,S端必须返回确认号并且同时发送一条SYN报文,此时进入SYN RCVD状态。

为啥要连带发送SYN报文?

TCP是全双工通信,协议规定当收到建立链接请求后必须返回序列号,同时建立本端到对端的通信链接。这也叫做捎带应答机制。

如果第二次报文丢失怎么办?

在发送完ACK+SYN报文后会启动一个定时器,超时没有收到ACK确认,会再次发送,会进行多次重试。超时时间依旧每次翻倍,重试次数可设置。

修改 /proc/sys/net/ipv4/tcp_synack_retries 的值

第三次:C端收到S端发的ACK+SYN报文,需要返回一个应答ACK的报文,此时该连接会进入半连接状态的队列,当S端收到ACK后,一条完整的全双工TCP链接建立完成,双方进入ESTABLISHED状态。

这里有个常用攻击手段,攻击者伪造一个SYN请求发送给服务端,服务端响应之后,会收不到C端的ACK确认,服务端会不断的重试,默认会重试五次。

此时服务端会维持这个链接的所有资源,如果有大量这样的请求,服务端的资源会被耗完。

这就是DOS攻击。

如果第三次报文丢失怎么办?

S端在发出ACK+SYN报文后会启动一个定时器,在超时触发还没收到ACK就确认是丢失了,会重试一次发送。

这里面的每个状态都必须搞明白,面试官也超级爱问上面的状态转移。

龙叔还遇到过一个面试官问我用过socket编程么?问我用过哪些socket函数?

C端socket编程代码

//C端#define PORT  8080#define BUFFER_SIZE 1024int main(int argc, char **argv){    //定义IPV4的TCP连接的套接字描述符    int sock_cli = socket(AF_INET,SOCK_STREAM, 0);    //定义sockaddr_in    struct sockaddr_in servaddr;    memset(&servaddr, 0, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = inet_addr(argv[1]);    servaddr.sin_port = htons(PORT);       //连接服务器,成功返回0,错误返回-1    int ret = connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr));     //客户端将控制台输入的信息发送给服务器端,服务器原样返回信息,阻塞    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)    {           ret=send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送        recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收        fputs(recvbuf, stdout);    }     close(sock_cli); // 关闭连接    return 0;}

S端socket编程代码

int main(int argc, char **argv){    //定义IPV4的TCP连接的套接字描述符    int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);    //定义sockaddr_in    struct sockaddr_in server_sockaddr;    server_sockaddr.sin_family = AF_INET;    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);    server_sockaddr.sin_port = htons(PORT);     //bind成功返回0,出错返回-1    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)     //listen成功返回0,出错返回-1,允许同时监听的连接数为QUEUE_SIZE    if(listen(server_sockfd,QUEUE_SIZE) == -1)     for(;;)    {        struct sockaddr_in client_addr;        socklen_t length = sizeof(client_addr);        //进程阻塞在accept上,成功返回非负描述字,出错返回-1        int conn = accept(server_sockfd, (struct sockaddr*)&client_addr,&length);         //处理数据部分      ...    }     close(server_sockfd);    return 0;}

为什么需要三次握手建立链接,2次可以么,4次行不行?

这问题问的,面试官是咋了?在这明知故问的,整些有的没的。肯定是不行啊,RFC 标准就是这样写的啊。

可不敢这样回答啊,标准是说的三次握手建立链接,可没说四次不行啊。要是这样答,妥妥的会收到,同学我们今天的面试到此基本结束了,你回家等消息...

龙叔来说说这个问题,为什么不能两次?

如果第二次不发送SYN+ACK,只是发送确认应答消息ACK,会造成只能建立单向通信,而且不能应答。而TCP是全双工通信的,而且必须保证可靠性。

如果第二发送SYN+ACK,不用应答。此时会出现三种情况

一、二次握手失败,C端会重复发送SYN报文,等待对端发送确认报文,S端会保存tcp连接的所有资源,大量的这种情况会导致S资源耗尽。

二、二次握手成功,S收不到ACK会重复发送SYN+ACK报文。

三、二次握手完以后,双方以为连接建立成功,即可开始通信。假如此时连接并没有真的建立成功,S端开始发送消息,会造成网络拥堵发生。

为什么不能是四次?

四次其实原则上来说是可以的,就是把第二次的ACK和SYN分两次发送。在理论上是完全可以行得通的,但是TCP本着节约网络网络资源的前提。

还有一种是不拆开二次握手的捎带应答,三次握手之后C端继续发送SYN报文,其时这是徒劳的。第三次完成以后链接已经建立,后面无论多少次都是徒劳。

如果双方同时建立连接,会发生什么情况?

TCP同时建立链接

这就是双方同时建立链接的情况,情况还不错,反正能建立成功,这点是肯定的。但是要注意两点

第一、此时只会建立一条全双工的TCP链接,不是两条。

第二、双方没有CS之分,两端都是同时承担两个角色,客户端和服务器。

四次挥手断开链接

先整个图看下四次挥手的整个过程和状态转移。状态转移会考看仔细点。

四次挥手断开链接

依旧采用C/S模式解释此过程。

第一次:当C端的应用程序结束数据传输是,会向S端发送一个带有FIN附加标记的报文段(FIN表示英文finish),此时C端进入FIN_WAIT1状态,C端不能在发送数据到S端。

第二次:S端收到FIN报文会响应一个ACK报文,S端进入CLOSE_WAIT状态。进入此状态后S端把剩余未发送的数据发送到C端,C端收到S端的ACK之后,进入FIN_WAIT2状态。

同时继续接受S端传输的其他数据包。

第三次:S端处理完自己待发送的数据之后,也会发送FIN断开链接的请求,S端进入LAST_ACK状态。

第四次:C端收到S端的断开链接请求后会启动一个定时器,该定时器时长是2MSL(最大段报文生存时间),同时发送最后一次ACK报文。

为什么要四次挥手?

TCP是全双工的通信机制,每个方向必须单独进行关闭。

TCP传输连接关闭的原则如下:

当一端完成它的数据发送任务后就可以发送一个FIN字段置1的数据段来终止这个方向的数据发送;当另一端收到这个FIN数据段后,必须通知它的应用层 对端已经终止了那个方向的数据传送。

为什么不能用三次握手中捎带应答机制减少一次握手?

这点到是很迷惑人,但是掌握了TCP传输的一些细节就会发现并不难。

TCP是全双工通信的,S收到断开链接请求后只是表示C端不会传输数据到S端了,但是并不表示S端不传输数据到C端。

如果采用捎带应答,S端将无法把剩余的数据传输到C端。

为何最后一次ACK之后需要等待2MSL的时间?

网络是不可靠的,TCP是可靠协议,必须保证最后一次报文送达之后才能断开链接,否则会再次收到S端的FIN报文信息。

而等待2MSL时间就是为了保证最后最后一次报文丢失时还能重新发送。

为何是2MSL的时间?

2MSL是报文一个往返的最长时间,假设小于这个时间会发生,ACK丢了,但是还没接收到对方重传的FIN我方就重新发送了ACK。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

这个不难TCP自己做了保证,TCP默认有个定时器,每次收到客户端的请求后会把定时器设置好,通常设置两小时,超过两小时还没收到数据。

服务端会发送一个探测报文,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

总结

三次握手和四次挥手的知识基本告一段落了,就讲到这里了,如果有什么不明白的地方可以加我微信探讨。

后面还会出一篇网络编程常用的linux命令行工具,比如ping、tcpdump、netstat、nc等等,在出一篇计算机网络的总结文章。计算机网络这部分基本完结了,如果又不懂得可以看看公号里面前面的文章。

本文使用 mdnice 排版

原文地址:https://www.cnblogs.com/zhonglongbo/p/12701607.html

时间: 2024-10-04 12:02:13

面试官求你了,别再问我TCP的三次握手和四次挥手的相关文章

以后面试官再问你三次握手和四次挥手,直接把这一篇文章丢给他

三次握手和四次挥手是各个公司常见的考点,也具有一定的水平区分度,也被一些面试官作为热身题.很多小伙伴说这个问题刚开始回答的挺好,但是后面越回答越冒冷汗,最后就歇菜了. 见过比较典型的面试场景是这样的: 面试官:请介绍下三次握手 求职者:第一次握手就是客户端给服务器端发送一个报文,第二次就是服务器收到报文之后,会应答一个报文给客户端,第三次握手就是客户端收到报文后再给服务器发送一个报文,三次握手就成功了. 面试官:然后呢? 求职者:这就是三次握手的过程,很简单的. 面试官:...... (番外篇:

硬不硬你说了算!35 张图解被问千百遍的 TCP 三次握手和四次挥手面试题

每日一句英语学习,每天进步一点点: 前言 不管面试 Java .C/C++.Python 等开发岗位, TCP 的知识点可以说是的必问的了. 任 TCP 虐我千百遍,我仍待 TCP 如初恋. 遥想小林当年校招时常因 TCP 面试题被刷,真是又爱又狠…. 过去不会没关系,今天就让我们来消除这份恐惧,微笑着勇敢的面对它吧! 所以小林整理了关于 TCP 三次握手和四次挥手的面试题型,跟大家一起探讨探讨. TCP 基本认识 TCP 连接建立 TCP 连接断开 Socket 编程 PS:本次文章不涉及 T

硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题

前言 不管面试 Java .C/C++.Python 等开发岗位,?TCP?的知识点可以说是的必问的了. 任 TCP 虐我千百遍,我仍待 TCP 如初恋. 不会没关系,今天就让我们来消除这份恐惧,微笑着勇敢的面对它吧! 于是我整理了关于?TCP 三次握手和四次挥手的面试题型,跟大家一起探讨探讨. TCP 基本认识 TCP 连接建立 TCP 连接断开 Socket 编程 PS:本次文章不涉及 TCP 流量控制.拥塞控制.可靠性传输等方面知识,这些留在下篇哈! 正文 01 TCP 基本认识 瞧瞧 T

简析TCP的三次握手与四次分手(TCP协议头部的格式,数据从应用层发下来,会在每一层都会加上头部信息,进行封装,然后再发送到数据接收端)good

2014-10-30 分类:理论基础 / 网络开发 阅读(4127) 评论(29) TCP是什么? 具体的关于TCP是什么,我不打算详细的说了:当你看到这篇文章时,我想你也知道TCP的概念了,想要更深入的了解TCP的工作,我们就继续.它只是一个超级麻烦的协议,而它又是互联网的基础,也是每个程序员必备的基本功.首先来看看OSI的七层模型: 我们需要知道TCP工作在网络OSI的七层模型中的第四层——Transport层,IP在第三层——Network层,ARP在第二层——Data Link层:在第二

面试三次握手、四次挥手

建立TCP需要三次握手才能建立,而断开连接则需要四次挥手. https://www.cnblogs.com/thrillerz/p/6464203.html ACK:是用来应答的 SYN:是用来同步的 FIN:终端连接请求 三次握手建立连接 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源.Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了. 四次挥手断开连接 假设Client端发起中断连接请求,也

字节跳动面试官揪着源码一直问,然后......

最近,我的一位朋友在找工作,已经拿到了美团.快手等公司的Offer,准备选择其中一家入职了.后来他又接到了字节跳动的电话,通知他去参加三面.从二面到三面之间隔了挺久的,他以为都没戏了,结果就收到了通知.由于已经拿到了很多大厂的Offer,他对这次面试还是挺有信心的.但是回来之后,表现的特别沮丧.一问之下,原来是面试官问的一些问题他回答的不好,他说面试官揪着源码一直问,但是自己并没有深入看太多的源码,所以回答的不是太好.他给我总结了一下面试官关于源码部分的问题,主要题目如下:1.你看过那些源码吗?

redis相关原理及面试官由浅到深必问的15大问题(高级)

1.redis是什么? redis是nosql(也是个巨大的map) 单线程,但是可处理1秒10w的并发(数据都在内存中) 使用java对redis进行操作类似jdbc接口标准对mysql,有各类实现他的实现类,我们常用的是druid 其中对redis,我们通常用Jedis(也为我们提供了连接池JedisPool) 在redis中,key就是byte redis的数据结构(value): String,list,set,orderset,hash 每种数据结构对应不同的命令语句~ 2.redis

入职四个月后,我当了一回面试官,面试了一名二本院校学生,结果。。。。

本文首发于微信公众号:程序员乔戈里 简历 1.首先说说简历,好多人都问过我,乔哥,进入bat这样的大厂是不是学历都需要211,985 其实,不是的,这次我面试的这个同学就是二本院校出生,我身边认识的 苦逼的码农 和 编程剑谱 的号主都是二本出身,也都拿下了BAT的offer,所以BAT这样的大厂并不是一定要211,985学历.继续说回到这位同学吧,这位同学虽然是二本,但是简历还是通过层层筛选,进入到了面试环节,肯定有一些过人之处.我看了看这位同学的简历,果不其然,还是很有料的. 我感觉可以用 光

百度面试两板斧:手写算法问基础

阅读本文大概需要 4 分钟. 作者:黄小斜 17年7月份,我参加了百度的实习生面试,随后在百度开始了半年的实习生活,18年7月份,我参加了百度的校招提前批面试,由于可以同时参加百度多个部门的提前批面试,结果我前前后后面试了10多次,也算是一段比较奇葩的经历了. 当然,实习生面试是这里面最简单的一次了,三轮面试,前两轮都是在问基础,问的也不深入,第三轮面试则直接谈人生谈理想.其实百度的日常实习生面试难度确实比校招要来的容易,因为百度一年四季都在招实习生,反观阿里和腾讯,只有在春招期间招收实习生.