TCP为什么要三次握手,不是两次四次

本文通过革命战争中的通信故事,形象地解释了TCP三次握手的原因。强调了三次握手的重要性,即确保双方都能正常收发信息,避免资源浪费。

TCP为什么要三次握手,不是两次四次?

第三次握手——革命斗争中的通信故事

在革命战争影片中,经常会看到英勇的解放军战士背着步话机在喊“长江长江,我是黄河,听到请回答。”很明显,这是呼号为黄河的一方想找呼号为长江的另一方说事,为了保证影片的节奏,导演往往把后面的联络过程省略了,其实后面还有两步,长江听到黄河的呼叫后要回答“黄河黄河,我是长江,我听到了你,请回答。”这叫做第二次握手。黄河听到长江的呼叫后还要回答“长江长江,我是黄河,我听到了你,现在请你收报”,这是第三次握手,三次握手成功后,才能在电台里传送工作报文。为什么必须三次握手后才能工作呢?听我慢慢道来。

 

《英雄儿女》

第一次握手:黄河发起呼叫,长江收到了。这时长江可以确认的是,黄河的发信机和自己的收信机是好的,否则的话他收不到黄河的呼叫;黄河能确认什么呢?他什么也不能确认,有可能自己的电台除了指示灯是好的,其它都是坏的,他在对着一台铁疙瘩发功。

第二次握手:长江回应,黄河收到了。这时黄河可以确认的是,自己和长江的收发信机都是好的,否则的话他收不到长江的回应信号。这时黄河可以说正事了吗?还不能,虽然长江发出了回应,但他并不能确认自己的发信机和黄河的收信机都是好的。

第三次握手:黄河对长江的回应进行回应。这时黄河很清楚,双方收发信机都是好的,自己的这次回应长江肯定能收到,这个回应的目的只是消除长江对黄河的收信机和长江自己的发信机的担心。然后,黄河不必等长江的再次回应就可以说正事了。

有些读者看到这里,心里会想“且慢,你凭什么说第三次握手后,长江肯定会收到?长江刚才的状态好,不代表后来的状态好,俗话说人算不如天算,我看再多握几次手会更可靠些。”这种想法有道理,几句话还真说不清,那么不妨先讲一个与此有关的战斗小故事。

战场态势是这样的:

驻扎在两个山头上的红1团和红2团分别有两个营,而在山谷的蓝团有三个营,若红1团和红2团孤军下山作战会失败,而两个团同时进攻就会胜利,对于红方来说,问题的关键是要同时发起进攻。

战斗前的准备过程是这样的:

红1团团长找了个传令兵,命令他跑到红2团,告诉红2团团长明早9时发起进攻。传令兵没有被蓝团俘虏,成功地跑到了红2团的山头,告诉红2团团长明早9时两个团同时进攻。红2团团长一边握着传令兵的手激动地说“太好了!我早就等着这个消息呢!”,一边心里暗自核计“虽然我知道了这个消息,但是红1团的团长并不知道我已经知道了,谁都知道传令兵有可能被俘,消息很有可能传不过来,若明天总攻前,红1团团长不知道我已经得到了消息,他一定不会贸然进攻的,换成我也不会”。于是,红2团团长对传令兵说“兄弟,你辛苦了,先抽袋烟吧,抽完后再辛苦你跑回去,告诉你们团长,说我已经得到消息了,明天按时总攻。”

传令兵又跑回到红1团,侥幸又没被蓝团俘获,他告诉红1团团长有关红2团团长已经获知明早进攻的消息,但红1团团长明早敢发起攻击吗?他不敢,因为他心里清楚,红2团团长并不知道他(红1团团长)已经知道了“红2团团长已获知明早9时发起攻击”这个消息,红1团团长继续想,如果是他本人是红2团团长,就不会贸然攻击,因为自己已经获知明早9时发起攻击这件事,对方未必知道,而有可能因为传令兵回团时被抓,敌人反而知道了,自己贸然攻击,有可能就会失败。于是,红1团团长对传令兵说“兄弟,你辛苦了,先抽袋烟吧,抽完后再辛苦你跑一趟红2团,告诉他们团长,就说我已经知道他已经知道了明早9点进攻这件事,让他放心地打吧。”

写到这里,聪明的读者已经猜到了,即使传令兵再次来到红2团,红2团团长也不敢开战,还是要传令兵再次回红1团报信,因为红2团团长担心红1团团长不知道他(红2团团长)已经知道了红1团团长知道他(红2团团长)知道明早9点发生攻击的事。(此句53个字,想到松鼠们多用短句的建议,心里严重疚结中……)

回到三次握手问题上,红1团和红2团其实就是通信的双方,这场永远也达不成协议的战斗,说明了一个重要的通信道理:世界上不存在完全可靠的通信协议

“三次握手”是电台点对点通信的一般规则,但即使三次握手成功后,以后的通信就能保证正常吗?当然不能,握手成功后的设备故障、干扰、话务员的伤亡等,无数种可能性会导致通信失败。三次握手成功只说明了之前的通信条件和环境,而不能决定和预测之后的通信条件和环境。根据经验,两个电台之间的通信失败,最大的可能性是两个电台本身的故障,三次握手的成功排除了这种可能。以后的情况很难预测的,通信协议只能做到尽可能的可靠,而不能做到理论上的完全可靠。

电台通信一定要三次握手吗?当然不一定。潜伏者为了不暴露自己,可以只使用收信机而不发信,回应总部时可以采用其它手段,例如在指定电线杆上贴个寻人启示。如果电台的发信机出了故障,也可能不经过三次握手而达成通信,在对越自卫反击战中,某部的电台发信机坏了,指挥所在没有收到对方握手信号的情况下,连续盲发14份报文,该部全部收到,并按指示顺利地完成了任务,该电台台长因此荣立了二等功。

 

如果是两次:

 

C发送请求,S应答并分配资源 
若S的应答没有到达C端,C认为连接未建立,而S认为建立了
S会在一段时间内保留分配的资源
如果大量C这样请求,S会崩溃 
TCP 三次握手的主要目的是确保通信双方能够可靠地建立连接,避免资源浪费和错误连接。握手过程中,客户端和服务器通过交换信息来确认彼此的发送和接收能力,确保数据传输的可靠性与安全性。 ### 三次握手的过程 1. **第一次握手**:客户端发送 SYN(同步)标志为 1 的报文段,包含一个随机生成的序列号 `seq=x`。这一步表明客户端希望建立连接,并告知服务器自己的初始序列号。 2. **第二次握手**:服务器回应 SYN-ACK 报文,包含 SYN 标志为 1 和 ACK(确认)标志为 1 的报文段。服务器发送自己的随机序列号 `seq=y`,同时将确认号 `ack=x+1` 发送给客户端,表示期望收到客户端的下一个序列号。 3. **第三次握手**:客户端发送 ACK 标志为 1 的确认报文段,确认号 `ack=y+1`,表示期望收到服务器的下一个序列号。此时,连接正式建立,双方可以开始数据传输。 ### 为什么三次握手,而不是两次两次握手无法确保服务器能够确认自己的发送能力和客户端的接收能力。在两次握手的情况下,服务器发送 SYN-ACK 后,客户端可能无法确认服务器的发送能力是否正常,也无法确认自己的接收能力是否正常。这可能导致服务器进入已连接状态,但客户端并未真正建立连接,从而浪费服务器资源。 例如,假设客户端发送的 SYN 报文在网络中延迟,客户端超时后重新发送 SYN。如果第一次 SYN 报文最终到达服务器,服务器会误认为这是一个新的连接请求并发送 SYN-ACK。然而,客户端并未响应,导致服务器一直等待,浪费资源。这种情况被称为“历史连接的混淆”[^5]。 ### 为什么三次握手,而不是四次四次握手虽然理论上可以增加更多的确认步骤,但并不会显著提高可靠性,反而会增加连接建立的时间和资源消耗。三次握手已经足够确保双方的发送和接收能力正常,因此没有必要增加额外的握手步骤。三次握手的设计在效率和可靠性之间取得了平衡。 ### 三次握手的意义 三次握手的设计确保了以下几点: 1. **防止历史连接的混淆**:通过三次握手,服务器可以确认客户端的响应能力,避免因旧的连接请求而导致的资源浪费。 2. **确认双方的发送和接收能力**:三次握手确保了客户端和服务器都确认了彼此的发送和接收能力[^3]。 3. **同步初始序列号**:双方通过交换初始序列号,确保数据传输的可靠性。 ### 示例代码 以下是 TCP 三次握手的一个简化示例代码,展示了客户端和服务器如何通过三次握手建立连接: ```python import socket # 服务器端 def start_server(): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 12345)) server_socket.listen(1) print("Server is listening...") client_socket, addr = server_socket.accept() print(f"Connection from {addr}") # 第二次握手:接收SYN并发送SYN-ACK syn_ack = client_socket.recv(1024) print("Received SYN:", syn_ack) client_socket.sendall(b'SYN-ACK') # 第三次握手:接收ACK ack = client_socket.recv(1024) print("Received ACK:", ack) print("Connection established.") client_socket.close() server_socket.close() # 客户端 def start_client(): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('localhost', 12345)) # 第一次握手:发送SYN client_socket.sendall(b'SYN') # 接收SYN-ACK syn_ack = client_socket.recv(1024) print("Received SYN-ACK:", syn_ack) # 第三次握手:发送ACK client_socket.sendall(b'ACK') print("Connection established.") client_socket.close() # 启动服务器和客户端 if __name__ == "__main__": import threading server_thread = threading.Thread(target=start_server) server_thread.start() start_client() server_thread.join() ``` ###
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值