TCP协议底层结构详解

一. TCP套接字的I/0缓冲

我们知道,TCP 套接字的数据收发无边界。服务器端即使调用1次write函数传输40字节的数据,客户端也有可能通过4次read函数调用每次读取10字节。但此处也有一些疑问,服务器端一-次性传输了40字节,而客户端居然可以缓慢地分批接收。客户端接收10字节后,剩下的30字节在何处等候呢?是不是像飞机为等待着陆而在空中盘旋一样,剩下30字节也在网络中徘徊并等待接收呢?

实际上,write 函数调用后并非立即传输数据,read 函数调用后也并非马上接收数据。更准确地说,如下图所示,write函数调用瞬间,数据将移至输出缓冲;read函数调用瞬间,从输入缓冲读取数据。

假如,下面两台的输入缓存大小为50字节,而我们输出缓存为60字节。现在右边的电脑要给左边的电脑传1000字节的数据。
1) 首先右边的电脑就会先写入60字节的数据到输出缓冲区内,然后就按一定速度传到右边的电脑接收缓冲区也就是输入缓冲,假入右边的电脑要读取500字节的数据,它并不会一下就读取到500字节的数据,因为它的输入缓存最大为50字节,所以它只能先读取50字节。而左边的电脑读取输入缓存和右边的电脑写入输入缓存都有一定时间。而这个时间差越小越好。

2)因为当右边电脑的发现电脑输入缓存为空,那么它就会继续写入60字节的数据到输出缓冲区内,然后根据TCP协议的低层设计 ,右边电脑就会先询问一下左边电脑,它的输入缓存是否还有位置,如果有,那么右边电脑就会把数据传输到左边电脑输入缓存区, 如果没有位置了那么他就会在那里等待,然后隔一段时间进行上诉操作。如果左边的read操作远大与右边的电脑write操作,那么右边电脑就会长时间处于等待,询问状态。
所以我们在设计服务器的时候,常常会单独使用一个线程来进行read操作,把读取到的数据储存在用户区缓存,而用户用户区缓存由于可以在堆内分配,内存比较大,由此来保证输入缓存大部分时间为空的状态,从而提高传输效率。
在这里插入图片描述

二.TCP的内部原理

TCP通信三大步骤:

1.三次握手建立连接;
2.开始通信,进行数据交换;
3.四次挥手断开连接;

1.三次握手;

【第一次握手】主机A∶"你好,主机B。我这儿有数据要传给你,建立连接吧。"
【第二次握手】主机B∶"好的,我这边己就绪。"
【第三次握手】主机A∶"谢谢你受理我的请求。"
简单描述:
(1)首先客户端A会给服务器B发送一个请求连接的请求,然后服务器B会根据自身情况来决定是否连接,
(2)如果可以连接,那么服务器B就会给户端A一个响应,如果客户端A接收到了服务器B的响应后就会与服务器B建立连接,
(3)然后客户端A就会给服务器B一个响应,告诉服务器B我这里准备就绪了,可以建立连接了,如果服务器B接收到了客户端A响应后就可以与客户端A建立连接,最后两者相互之间建立了连接,就可以进行通讯了。

这其中的具体消息传输,我们可以看下图。
在这里插入图片描述
首先:
【第一次握手】
[SYN] SEQ:1000, ACK:-
该消息中SEQ为1000,ACK为空,而SEQ为1000的含义如下∶
主机A:“现传递的数据包序号为1000,如果接收无误,请通知我向您传递1001号数据包(也就是第三次握手的的发送的SEQ为1001)。“这是首次请求连接时使用的消息,又称SYN。SYN是Synchronization的简写,表示收发数据前传输的同步消息。
【第二次握手】
接下来主机B向A传递如下消息∶[SYN+ACK]SEQ:2000, ACK:1001
此时SEQ为2000,ACK为1001,
SEQ为2000的含义如下–主机B∶"现传递的数据包序号为2000如果接收无误,请通知我向您传递2001号数据包。”
注意:TCP连接过程中发送数据包时需分配序号。在之前的序号1000的基础上加1,也就是分配1001,简单来说,需要ACK对别人发送的数据包做出处理。
所以ACK:1001的含义如下–主机B∶"刚才传输的SEQ为1000的数据包接收无误,现在请你传递SEQ为1001(上次客户端传输的SEQ+1)的数据包。”
对主机A首次传输的数据包的确认消息(ACK:1001)和为主机B传输数据做准备的同步消息(SEQ2000)拥绑发送,因此,此种类型的消息又称SYN+ACK。至此,主机A确认了主机B准备就绪。
注意:收发数据前向数据包分配序号,并向对方通报此序号,这都是为防止数据丢失所做的准备。通过向数据包分配序号并确认,可以在数据丢失时马上查看并重传丢失的数据包。因此,TCP可以保证可靠的数据传输。最后观察主机A向主机B传输的消息︰
【第三次握手】
[ACK]SEQ:1001, ACK:2001
此时该数据包传递如下消息,
SEQ为1001的含义如下∶"现传递的数据包序号为1001(对应第二次握手的ACK)如果接收无误,请通知我向您传递1002号数据包(数据通讯的数据包)。"
ACK:2001︰“主机A已正确收到传输的SEQ为2000的数据包,现在可以传输SEQ为2001的数据包。”
这样就传输了添加ACK:2001的ACK消息。至此,主机B确认了主机A准备就绪,所以此时主机A和主机B确认了彼此均就绪。

上面所讲的过程抽象到生活中就相当于这样一个场景:
TCP三次握手好比在一个夜高风黑的夜晚,你一个人在小区里散步,不远处看见小区里的一位漂亮妹子迎面而来,但是因为路灯有点暗等原因不能100%确认,所以要通过招手的方式来确定对方是否认识自己。
你首先【向妹子招手(syn)】,妹子看到你向自己招手后,向你点了点头【挤出了一个微笑(ack)】。你看到妹子微笑后确认了【妹子成功辨认出了自己(进入建立连接(established)状态)】。
但是妹子有点不好意思,向四周看了一看,有没有可能你是在看别人呢,她也需要确认一下。妹子也【向你招了招手(syn)】,你看到妹子向自己招手后知道对方是在寻求自己的确认,于是也【点了点头挤出了微笑(ack)】,妹子【看到对方的微笑后确认了你就是在向自己打招呼进入(建立连接(established)状态)】。
于是两人加快脚步,走到一起,愉快的相互交谈(进入TCP通讯的第二步)。

我们来回顾一下,这个过程中总共有四个动作,

1.你招手

2.妹子点头微笑
3.妹子招手

4.你点头微笑

这不是四次握手吗?为什么说是三次??
答案:如上图,2+3(ayn+acK)动作是两个动作的合并

2.四次挥手

挥手过程:
【第一次挥手】主机A∶"我希望断开连接。"
【第二次挥手】主机B∶"哦,是吗?请稍候。"
【第三次挥手】主机B∶"我也准备就绪,可以断开连接。
【第四次挥手】"主机A︰“好的,谢谢合作。”

简单描述:
假如主机A要与主机B断开连接,
【第一次挥手】首先主机A会给主机B发送请求断开连接的消息,
【第二次挥手】然后主机B对主机A发送的消息做出响应,表示已经收到了请求断开连接的消息,并向主机A发送消息,表示正在查看是否可以断开连接,此时主机B并没有处于可以断开连接状态。
【第三次挥手】主机B继续对主机A发送的消息做出响应,表示已经收到了请求断开连接的消息,并向主机A发送消息,表示准备好了,已断开连接。
【第四次挥手】主机A对主机B发送的【准备好了,已断开连接】的消息做出响应,断开与主机B的连接。

在这里插入图片描述
抽象到上次的场景中就是:
假如你和妹纸聊了很长时间,然后有事要离开了。

我:“对不起,我妈叫我回家了,我想分开了。”
妹纸:“好的,请稍后,我给我妈打个电话,确定一下能不能回家。”
妹纸:"我确定好了,可以回家,分开吧。
" 我:“好的,我走了”。

我们发现在断开连接的过程中,发送请求断开的一方需要两次等待,也就是第二,第三次挥手,比较耗时,所以我们在设计服务器的时侯,比如游戏服务器,动则成千上百万,在处理服务器请求与客户端断开连接时一般使用线程单独处理,而不再主进程进行处理。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值