按这样组网: A机器 <----> 交换机1 <----> 交换机2 <----> B机器,A、B机器上各一个进程,相互之间socket使用TCP方式传送消息。
有个需求是,当A机器数据变更后,需要发送一个广播通知消息至B机器(消息包比较大,几十K甚至1M以上)。但是有这样一种情况,就是如果 A机器正要 send 数据时,如果此时交换机之间的链路断了,由于 A、B机器都检测不到这个断链,因此 A机器调用 send 肯定是返回成功的。如果交换机之间的链路立即就恢复了(瞬断),那还好,TCP会将此数据正常传输至B机器;但是如果交换机之间的链路较长时间不恢复的话(例如1分钟左右),那就麻烦了,B机器将可能就收不到这个消息了,导致部分处理异常。
我查询了一下TCP传输的相关资料,TCP在传输数据时,为保证数据正确性,都有个包发送和确认(ACK)机制,如果发送数据后在重发超时到来之前都未收到确认信息,那么就会重发包,但是如果数次重发都未收到应答,就会直接将这个包丢弃。我的这个情况应该就是由于中间链路断得过久,导致包被丢弃了。
我想如果能够实现这么一个机制,既如果A机器发送数据后,如果发现包被丢弃的情况,那么就再重发,一直尝试发送成功为止,这样就能极大的避免通知丢失的问题。但是问题是:
1、如何判断本次发送的包丢失了?
2、怎样判断包正常的发送到了B机器(不能期望B机器应答消息,因为这个是通知,而且那个是另外一个公司开发的程序,无法修改)
有个需求是,当A机器数据变更后,需要发送一个广播通知消息至B机器(消息包比较大,几十K甚至1M以上)。但是有这样一种情况,就是如果 A机器正要 send 数据时,如果此时交换机之间的链路断了,由于 A、B机器都检测不到这个断链,因此 A机器调用 send 肯定是返回成功的。如果交换机之间的链路立即就恢复了(瞬断),那还好,TCP会将此数据正常传输至B机器;但是如果交换机之间的链路较长时间不恢复的话(例如1分钟左右),那就麻烦了,B机器将可能就收不到这个消息了,导致部分处理异常。
我查询了一下TCP传输的相关资料,TCP在传输数据时,为保证数据正确性,都有个包发送和确认(ACK)机制,如果发送数据后在重发超时到来之前都未收到确认信息,那么就会重发包,但是如果数次重发都未收到应答,就会直接将这个包丢弃。我的这个情况应该就是由于中间链路断得过久,导致包被丢弃了。
我想如果能够实现这么一个机制,既如果A机器发送数据后,如果发现包被丢弃的情况,那么就再重发,一直尝试发送成功为止,这样就能极大的避免通知丢失的问题。但是问题是:
1、如何判断本次发送的包丢失了?
2、怎样判断包正常的发送到了B机器(不能期望B机器应答消息,因为这个是通知,而且那个是另外一个公司开发的程序,无法修改)
象你的这种情况如果send的返回值<1,就重新建立连接发送数据。
如果真是这样,TCP不会有重发功能了。send()返回成功,只代表你要发送的数据被成功复制到系统winsock缓冲(也就是AFD.SYS本身的内部缓冲),并不代表你的数据成功发送给对方,对方并且成功接收到.至于后面的事情,就是系统本身在处理,如果发送失败,系统会再重发这些数据,但在应用层,你并不知道内核到底有没有重发或者重发是否也失败。虽然出现的机率很低,但TCP一样会出现发包失败的情况,而且应用层程序并不知晓失败了。
我等得心痒痒了,可否提示一下,因为老板催得急啊,呵呵。
因为当TCP发送失败导致丢包时,会自行产生一个 网络不可达(或端口不可达) 的ICMP消息,因此我有一个想法,就是发送数据后,先将发送消息缓存起来,然后设置一个超时时间(例如3分钟),如果在超时时间之内,收到ICMP的不可达消息,就重发事先缓存的消息,一直重复上述过程,直到在超时时间内都未收到ICMP不可达消息,就认为对方已经收到了,再将缓存的消息删除。
但是这样做的话,对于广播风暴的情况,可能导致进程中为缓存消息花费太大的内存,这样可能会导致程序内存耗尽而崩溃。所以不敢轻易采用此方法。
提示: 让接收方发送ACK确认包后,发送方的应用程序的send()才返回成功结果。
1ms,2ms,4ms,8ms,16ms等待重传一般以这种方式增涨
继续发送会导致发缓冲区溢出,可以在Send函数通过WSAGetLastError获得错误类型WSAENOBUFS还有一种情况就是你的等待时间已经超过了所能容忍的范围,会导致Tcp Socket错误,这个等待时间应该是个可配置的参数可以查找相关资料。