TCP与UDP的特点
TCP特点:
- 面向连接的
- 可靠的
- 流式服务
UDP特点:
- 无连接
- 不可靠
- 数据报
TCP与UDP并没有优略之分,都有相对合适的应用场景,TCP的连接是在客户端connect()
,发起连接,通过三次握手来建立连接,connect()
执行客户端就会向服务端发送一个TCP报文(不包含数据)
TCP的报文有5行,每行32位4字节,即报文有20字节,如果现在执行connect()
向服务端发送请求,其中的标志位,SYN = 1 代表是一个同步报文,若是断开则是 FIN 标志
当对方收到我们发送的请求连接报文,随后会对我们的报文进行回复,回复报文会将确认信息放在确认号上
TCP三次握手
发起连接的一边执行connect()
,随后发送连接报文,其中序号SYN seq = i
,当服务器段收到以后,会向其回复一个TCP报文,在这个报文中会描述自己的序号,也会有对应的确认号SYN seq = j, ACK = i+1
,seq是自己的序号,ACK回复i+1是对接收到的报文进行确认,上面两个报文中都没有数据,随后客户端接收到来自服务端的确认报文,会再发送一个TCP报文ACK = j+1
,来确认服务端的确认报文,这样三次握手就完成了,代表连接已经建立成功
TCP三次握手在系统底层实现,完成后会将其放在完成握手监听队列中,这样的连接才会得到accept()
处理
TCP四次挥手
TCP通过三次握手建立连接,通过四次挥手断开连接,四次挥手的开始实在服务器或者客户端执行close()
后,挥手依然是发送这样的TCP报头,我们需要通知关闭,FIN标志就会唯一有效
当我们有一方执行close()
后,就会给对方发送一个TCP报头并将FIN置为1意味有效,发送一个序号(FIN seq = i)这类消息意味着通知,被动断开方只能回复确认信息(ACK = i +1),这时候被动断开方也需要进行关闭,也向对方发送一个关闭通知(FIN seq = j),随后对方也会回复确认信息(ACK = j+1)
可能我们会有疑问,为什么不将下面这两种消息合并在一起发送呢?这是因为在对方接收到关闭通知需要进行回复,而它的关闭需要在执行close()
后才会发送关闭通知,如果我们一方在收到对方的关闭通知FIN的时候,恰好也执行了close()
,也需要向对方发送FIN,这时候这这步就会合并在一起,造成三次挥手
TCP连接的可靠性如何保证
TCP为了保证可靠性拥有以下几种机制:
- 应答确认
- 超时重传
- 滑动窗口
应答确认
即就是,当我发送一个数据,对方收到后需要给我一个确认信息,这属于底层协议自动发送,不需要用户进行发送,例如:我们向对方send("hello")
,对方会在底层发送一个确认信息,我们是看不到的,假如我们没有收到来自对方底层的确认信息,我们就不会将数据从缓冲区删除,因为在发送数据的过程中,很有可能会发生丢失(底层通过ip协议进行发送,ip协议本身就是不可靠协议),就需要我们重新向对方发送
超时重传
当发送方一定时间内没有收到来自接收方的确认回复,则发送方会认为数据在发送过程中丢失,从而再次发送数据;
同时也会出现对方成功接收到数据,但是确认回复丢失,发送方依然会认为发送失败,进行重新发送这样接收方会接收到两个同样的数据,我们接收方会根据报文的编号将重复的包进行删除
滑动窗口
滑动窗口进行流量控制,当我们正常情况下发送数据,需要得到接收方的确认回复,才能发送下一个数据,这种情况效率较低,滑动窗口就是:我们允许一次发送多个数据
红色的框就代表滑动窗口,我们连续发送三个数据(2,3,4),接收一个确认信息(2的确认信息),当我们接收到确认信息将滑动窗口后移并且发送数据5,并且等待接收数据3的确认回复,这样通过滑动窗口的大小来控制发送数据的流量大小,发送的太快也会因为对方缓冲区放满而丢失数据
这三种机制保证了TCP连接的可靠性
还有一点,我们先发送的数据可能后到达,而后发生的数据可能先到达,我们会根据TCP报头的序号进行重新排序,我没收到数据后会进行去重、排序然后再交给应用层来使用,这样我们的数据再发送前是什么样子,发送后依旧是什么样子
流式服务我们再上一篇博客中也进行了介绍,具体就是我们的发送接收都会有一个缓冲区来形成字节流以及从字节流获得数据,这样就造成了多条数据会一次发送即为粘包