沧州租房网:面试官求你了,别再问我TCP的三次握手和四次挥手
时间:5个月前 阅读:122 评论:1
少点代码,多点头发
本文已经收录至我的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字节的牢固长度,寄义如下:
-
源端口和目的端口
各占2字节,就是存储源端口号和目的端口的
-
序号seq
占4字节,示意的局限就是整形的局限[0~2^32]。序号使用在给数据部门每个字节举行编号的,编号方式是mod 2^32 。
-
确认号ack
占4字节,局限也是无符号整数的局限。使用在对端传输给我的数据最后一个字节序号,例如A传输给B 101—500,此时B返回简直认号一定是小于即是501的。当B段准确吸收数据之后才会返回确认号,换句话说确认号之前的数据已经所有吸收。
-
数据偏移
占4bit,数据偏移很多人很容易想到是不是示意数据的长度,那就错了。偏移嘛,指的是TCP起始位置到数据部门的起始位置的偏移,也就是TCP首部的长度。
-
保留
占6bit,保留字段顾名思义,就是为往后使用,默认置为0。
-
紧要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是个宏,
-
确认ACK
占1bit,当ACK=1时生效。TCP有条硬性规定,当确立链接乐成后所有传输的数据报文都必须把ACK置为1。
-
推送PSH
占1bit,发送方把PSH置为1时 会马上发送该数据包,吸收方收到PSH=1的报文会马上处置交付给应用层处置。是不是感受和URG很像,实在照样有些区别的。
-
两者相同点:
URG与PSH两者都使用于紧要处置的情形,用来快速传输紧要数据。
-
两者不同点
URG置为1时,对于发送发,“带外数据”与正常情形下应该发送的新闻数据一起,封装成数据报发送,省去了在行列中守候的时间。 在吸收方,剖析报文后,获取数据之后照样要放在缓存区中,守候满了之后在向上往应用层交付。
PSH置为1时,对于发送方,解释这些数据不需要等向下发送的缓存区满,马上封装成报文,发送,省去了守候发送缓存区到达满的状态的时间。 在吸收方,也不需要等接受缓存区满,直接向上交付给应用层。
-
复位RST
占1bit,当RST=1时,TCP会自动释放链接,两种情形会用上。
TCP泛起严重差错时,会自动释放毗邻,重修链接,传输数据。
遇到非法报文或者拒绝毗邻时会把RST置为1.
-
同步SYN
占1bit,同步控制位,用来在传输毗邻确立时同步传输毗邻序号。
SYN=1时,示意这是一个毗邻请求或毗邻确认报文。
SYN=1,ACK=0,解释这是一个毗邻请求数据段,若是对方赞成确立毗邻,则对方会返回一个SYN=1、ACK=1简直认。
-
FIN控制位
占1bit,用于释放一个传输毗邻。
FIN=1时,示意数据已所有传输完成,发送端没有数据要传输了,要求释放当前毗邻,然则吸收端仍然可以继续吸收还没有吸收完的数据。
FIN=0,正常传输数据。
-
窗口巨细
占16bit,2byte,用于示意发送方可以接受的最大数据巨细。
该窗口是动态转变的,用作流量控制时使用。
-
磨练和
占16bit,2byte,用于对TCP头部,伪头部,数据三个部门举行校验。
-
紧要指针
占16bit,2byte,用于纪录紧要数据的末尾在数据段中的位置。
当URG=1时,该指针才生效。
-
可选项
可选项最长可达40byte,是可选的,可以没有。当可选项不存在时,TCP头部长度为20byte。
可选项可以包罗窗口缩放选项(Window ScaleOption,WSopt)、MSS(最大数据段巨细)选项、SACK(选择性确认)选项、时间戳(Timestamp)选项等。
-
数据
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 1024
int 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链接,不是两条。
第二、双方没有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等等,在出一篇计算机网络的总结文章。计算机网络这部门基本完结了,若是有不明白可以看看公号内里前面的文章。
阳光在线www.sinotter.com(原诚信在线)现已开放阳光在线手机版下载。阳光在线游戏公平、公开、公正,用实力赢取信誉。
网友评论
AllbetGmaing代理
回复allbet gaming欢迎进入欧博平台网站(www.allbetgaming.net),www.aLLbetgame.us开放欧博平台网址、欧博注册、欧博APP下载、欧博客户端下载、欧博游戏等业务。此时此刻我在看