如何保证消息的可靠传输?

意思是:客户端把消息发送出去了,只要客户端这里显示他的消息发送成功,就要保证对端一定要收到,要么收不到,客户端就显示发送失败,用户后续选择重新发送消息。如果客户端显示消息发送成功,就一定保证对端一定收到这条消息。
我们可以在业务层实现消息的确认机制(结合心跳):在消息发送之后对端给予响应。
集群聊天服务器是基于TCP协议实现的,TCP协议本身是可靠的传输协议,在发送数据的时候,有超时重传机制,TCP发送每一个数据的时候,都会等待得到接收方返回的ACK消息确认,它如果得不到ACK,就会超时重传,选择重新发送这个数据,得到ACK确认之后,保证消息发送成功了。
为什么业务层上还要实现消息确认机制呢?
我们看看下面这张图:

这个send的返回值大于0,发送成功了,返回了发送成功的字节数,但是并不代表着这条消息跑到对端,对端接收到了。
send怎么可能是调用的时候还包括客户端往服务器发送的过程和服务器接收到了,然后响应,响应成功了,send才返回?不可能的!如果是这样的话,send调用所花费的时间就非常长了,尤其是在网络环境非常复杂的情况下,这样做,耗费的时间是非常长的。

当我们在进程的用户空间的数据,就是上图中的buf,然后它要发送,调用系统的接口send,把用户空间的buf数据拷贝到内核空间的TCP发送缓冲区中,因为TCP是流式服务,有TCP的发送缓冲区和接收缓冲区, UDP数据报协议是没有发送缓冲区和接收缓冲区的,对于TCP发送来说,send发送成功,仅仅只是代表说把用户空间的buf里面的多少数据拷贝到内核空间的TCP发送缓冲区中。还没有把数据发送到对端。

内核空间的TCP发送缓冲区的数据,是由内核的TCP协议栈(相当于是TCP协议的代码模块),专门把发送缓冲区的数据(根据滑动窗口机制,可以根据网络状况,调节发送缓冲区的数据的发送的一个快慢,调节流量,把数据发送出去)。
send只是把数据发送到TCP的发送缓冲区中,然后send就返回了,用户空间继续向下执行代码。所以,send成功返回,并不能说明消息到达对端了。

内核空间处理TCP发送缓冲区的数据,开启TCP的传输,TCP传输的时候,传1个数据报文,在这里,有可能出现2种情况:
情况1、从C端发送到S端后,由于网络的情况比较复杂,网络比较拥塞,数据报文经常切换网络节点的路由,导致这个数据报文的TTL在没有到达S端,已经超过上限了,直接被路由器把当前的数据报文丢弃了,到达不了S端了。
情况
情况 2、这个报文到达S端了,基于TCP的可靠传输,对每一个发送的数据报文,都要进行响应,这个ACK可能就碰到了情况1了,导致ACK报文在网络中丢了,没有到达C端。
上面2种情况都是属于报文传输失败了。

当C端第一次发送报文的时候,TCP协议栈里面就会起1个超时重传定时器,当我们发送的数据报文到达S端,S端返回的这个ACK也到达C端了,那我们就可以继续发送下一个报文了。
如果说,当我第一次发送该报文,超时重传定时器已经超时了,还没有得到S端的响应ACK,它就会重新去发送这个数据报文,力求这个S端可以返回这个报文的ACK,如果后面S端响应了这个ACK,说明这个报文传输成功,如果还是出现了数据报文没有发到S端,或者S端返回的ACK在中途丢了,C端没有收到,超过定时时间,它又要进行超时重传,超时重传是有上限次数的,是不可能无限次超时重传下去的,网络情况是很复杂的,如果网络情况复杂,搞了很多很多的TCP报文超时重传,一直不断重传,整个内核所占用的资源永远得不到释放,这是非常不好的。所以,超时重传超过一定次数,TCP会发一个RST(连接重置)的报文给到对端,整个超时重传流程就结束了。TCP的超时重传是在一定程度上保证了报文的可靠性传输,但是如果网络环境比较差,一直重传都没有得到响应,TCP发送一个RST连接重置报文就不管了,消息还是没有到达S端!!!所以,我们聊天消息的可靠传输肯定是不能靠协议来保证的。
**内核空间是不可能给用户提供一个回调函数提醒消息没有传输成功的。**尤其是在TCP协议栈,TCP协议栈是负责网络模块的,其本身就随着网络环境的复杂,TCP协议栈本身实现也很复杂,如果在这里给用户注册一个回调函数,万一回调函数里访问了一个空指针,直接把内核的TCP模块废掉了,怎么办?
Linux的信号signal,信号的回调操作内核发给你当前进程的信号,可以写代码让这个信号挂掉,因为信号是某个进程的信号,把进程搞挂掉,就是让这个进程不要运行了而已。但是TCP协议栈是给的用户空间的所有应用程序服务的,万一给你注册回调,把内核挂了,怎么办???

数据传输的过程:传输层(TCP)-》网络层(IP)-》数据链路层(MAC)
网络层以上是通过IP地址定位主机的,网络层以下(从链路层开始)是通过Mac2地址定位主机的。Mac是真真正正,把数据一帧一帧的发送到网络上了。Mac发送一帧的大小:上限是MTU 1500字节,TCP的报头是20字节,IP的报头是20字节,一般来说,一个网卡的一帧最多携带1500-40=1460字节数据。如果我们发送的数据的大小是超过1460字节的,我们在IP层可以进行分包,分片传输,分成1小片1小片,发送到对后,可以把这些小的分片组成原始的数据。

正确方法

我们还是得在业务层上实现消息的可靠传输!!!
我们把客户端要发送的消息都缓存起来,因为客户端要实现消息的可靠传输,而且,每一条消息都有seq序列号,不管是一对一聊天,还是群聊,每一个对象在每一个聊天的会话都有消息的序列号seq。
客户端A连续发送3条消息:
message1 seq:0
message2 seq:1
message3 seq:2
0号消息先发送出去,正常情况下,这条消息发送出去以后,客户端要等待服务端对这个消息的响应ACK,然后从正常流程来说,客户端发送1号消息。
以此类推。
或者说,在客户端中,也没有必要按照顺序发送消息,为了性能来说,可以把消息全部发送出去,因为每条消息都有seq序列号,在对端收到消息以后,会对消息进行缓存,按顺序显示消息,所以发送方可以一次性全部发送出去,但是发送方一定要等待每个消息回应的ACK,哪个消息回应了ACK,就说明哪个消息是可靠传输到了对端(因为消息都有序列号,可以区分出来),客户端发送到服务器,服务器转发到另一个客户端上,都是根据这个方法。如果客户端收到某条消息的ACK了,就可以把这条消息从本地缓存删除了,说明这个消息后面不用进行重传了。如果客户端得不到对1号消息的ACK的话,我们可以在客户端设置超时重传机制,如果3毫秒没有收到这条消息的ACK,再重新发这条消息,以此类推,如果重复传3次还没有收到这条消息的ACK,我们就给客户端显示:该消息发送失败。等下一次网络恢复,由用户来选择是否重传。

客户端也可以通过实现心跳机制来检查服务端的连接能不能连通,心跳正常,发送消息肯定没问题。比如说,客户端隔1秒发送一次心跳,如果服务端没有响应,客户端的心跳计数就从0加到1,如果收到服务端的响应,就减1,以此类推,如果心跳计数超过3,客户端就判定和服务端的连接断开了,客户端就是红色状态了。如果客户端没有退出APP的话,就一直发送心跳,如果发着发着,网络恢复了,客户端发了一个心跳,服务端响应了,客户端把心跳计数的3改为0,继续发送心跳,如果刚恢复了,现在又出现问题了,心跳计数又超过3了,又判定和服务端连接有问题了
也就是说,心跳给了和服务端的连接状态,我们发送消息的时候可以先看连接的状态,如果是OK的话,心跳消息都能交互成功,我们发送普通的消息肯定也可以收到ACK响应的。如果心跳已经是失败的,我们在全局有一个状态(和服务器的状态)已经不可用的状态(inactive),我们客户端在发送消息就直接是不能发送,直接给用户显示发送失败,不用尝试发送了,心跳都失败,发送普通消息还能成功???等下一次心跳恢复正常的话,我们再去发送消息。

我们在TCP,UDP都会设置心跳机制来复杂的网络环境中,客户端检测服务端链路是否正常,服务端检测客户端是否在线。

670-聊天服务器和客户端如何保证消息的可靠传输相关推荐

  1. 400-集群聊天服务器的客户端开发

    我们之前把聊天服务器的代码基本上功能开发完了,在后面转成集群版本的时候要引入中间件-基于发布订阅的Redis. 现在我们先开始客户端的开发 我们聊天服务器项目工程的客户端和服务器会共用很多代码,所以把 ...

  2. 用python模拟多人聊天服务器以及客户端(带图形化界面)

    所用python的知识点: 网络编程:socket 多线程:threading 图形化编程:tkinter 首先要模拟出一个服务器,以供客户端连接,注意这里的要用死循环,要让服务区一直处于开放的状态 ...

  3. IM系统中如何保证消息的可靠投递(即QoS机制)(转)

    消息的可靠性,即消息的不丢失和不重复,是im系统中的一个难点.当初qq在技术上(当时叫oicq)因为以下两点原因才打败了icq: 1)qq的消息投递可靠(消息不丢失,不重复) 2)qq的垃圾消息少(它 ...

  4. 使用python中的socket实现服务器和客户端,并完成图片的传输

    使用python中的socket实现服务器和客户端,并完成图片的传输. 2018年03月09日 16:05:23 阅读数:301 socket服务器代码: [python] view plaincop ...

  5. 消息队列面试 - 如何保证消息的可靠性传输?

    消息队列面试 - 如何保证消息的可靠性传输? 面试题 如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题? 面试官心理分析 这个是肯定的,用 MQ 有个基本原则,就是数据不能多一条,也不能少一条 ...

  6. TCP是如何保证数据的可靠传输的

    TCP是如何保证数据的可靠传输: 1.TCP在传输有效数据之前要求通信双方必须先握手,建立通信才可以进行数据传输! 2.TCP的接收在接收到数据包之后会发送ack(可以理解为"回复消息&qu ...

  7. 服务器端和客户端互发消息,Socket编程实现简单的服务器与客户端互发消息

    socket编程的大致步骤如下: 1.创建服务器端SocketServer,并定义SocketServer的监听端口; 2.ServerSocket调用accept( )方法,是指处于阻塞: 3.创建 ...

  8. python 消息 推送服务器,从客户端发送字符串消息到服务器Python

    我无法运行我的代码,只要我将这些发送线添加到客户端和服务器中的recv行.我不知道什么是错的.没有他们,程序运行完美.我为了便于调试而粘贴了尽可能小的代码,因为如果没有这些代码,代码是完美的.从客户端 ...

  9. php 通知客户端,PHP+SSE服务器向客户端推送消息

    SSE与WebSocket作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息. 但是WebSocket比SSE强大很多,SSE只能作为一个轻量级的消息推送方案,解决了从服务端向 ...

最新文章

  1. 【spring】自动装配
  2. webpack源码之tapable
  3. vscode编辑器自动格式化
  4. Android跳转intent简单教程
  5. Apache2.2+tomcat7 负载均衡配置
  6. (0009) iOS 开发之友盟统计分析SDK已全面支持HTTPS的更新
  7. Windows Phone 7 学习网址总结
  8. 从0到1建立一张评分卡之可视化分析
  9. SD从零开始16 促销计划(Agreements)
  10. android getMemoryClass()的使用
  11. 5.5的performance_schema
  12. 有机物燃烧的化学方程式配平(洛谷P1994题题解,Java语言描述)
  13. udf提权 udf.php,UDF提权
  14. linux-基本开发环境搭建
  15. Windows开机自动启动Virtual Box虚拟机(官方指南手册)
  16. HDU 2234 无题I
  17. Android mainfests手记
  18. C语言编程:如何计算二叉树叶子结点数目?
  19. 回顾+纪念:离开帝都的第一年
  20. keras如何自定义损失函数(进阶版,not of the form of f(x_true, x_pred))

热门文章

  1. heic格式怎么转换成png?
  2. html文本框左上角输入文字,[css]将textarea前的文字设置在左上角
  3. python中的line.rstrip()
  4. 电商工具箱之淘客检测
  5. 2021年化工自动化控制仪表考试题及化工自动化控制仪表证考试
  6. 聊天室技术内幕ABC.
  7. 幼儿园语言活动包括哪几类_语段从语言三个方面-幼儿园语言教育的内容包含哪些请从三方面进行回答?幼儿园语言教育的 爱问知识人...
  8. Kettle安装及简单使用
  9. 解决Windows丢失msvcp120.dll问题
  10. 使用Arduino控制直流电机