Chinaunix

标题: linux下tcp协议的socket可靠传输 [打印本页]

作者: notsureit    时间: 2011-10-20 11:21
标题: linux下tcp协议的socket可靠传输
tcp协议是可靠的,socket只是实现的一种具体方式,并不代表我们使用socket编写的应用程序一定是传输可靠的。
   
  我现在遇到的问题是:
  一个简单的单向上传协议,使用send不停的向tcp server发送数据包,可以理解为一个大文件的上传,由于send、write等函数仅仅是把待发送数据拷贝到发送缓冲区,并不是真正的发送成功,因此,在网络出现瞬时断开时,发送缓冲区中的数据可能无法继续发送成功,但是由于是单向协议,没有服务端的收包确认,应用程序本身也不知道发送缓冲区中还剩余哪些数据没有发送成功,造成实际上的丢包
   
  请问大家,如何确保发送缓冲区中数据成功送达tcp server,而不:
1)修改应用协议
2)降低传输效率
  之所以提到2,是因为我采用
  1.    
  2. int nFixedSndBufSize = 0; struct timeval stTimeval; setsockopt(hSockFD, SOL_SOCKET, SO_SNDBUF, &nFixedSndBufSize, sizeof(int));
复制代码
强制设置发送缓冲为零时,可能能确保成功,但是发送效率急剧下降。
作者: l2y3n2    时间: 2011-10-20 11:26
什么意思?

send的返回值不能判断成功与否么?
作者: notsureit    时间: 2011-10-20 11:28
回复:l2y3n2
不可以,只能代表成功的放置到发送缓冲区中了
作者: zylthinking    时间: 2011-10-20 12:26
我忘记有没有相应的接口了, 但lz可查询一下有没有可用的发送缓冲的 opt, 配合sndbuf 总大小, 应该可以知道哪些发出去哪些没有了;
作者: zylthinking    时间: 2011-10-20 12:30
或者不管三七二十一, 直接重复发送断线之前已经send的若干个包, 只要重传的数据大小超过 协议栈 发送buf 的大小, 就能保证不丢包, 让服务器自己排重就可以了
作者: notsureit    时间: 2011-10-20 15:20
多谢 zylthinking
在同事和您的双重提醒下,我找到了解决方法:
  1.     size_t ileftUnsend;

  2.         if(ioctl(hSockFD, SIOCOUTQ, (char*)&ileftUnsend) < 0)
  3.         {
  4.                 perror("ioctl SIOCOUTQ error:");
  5.                 return FALSE;
  6.         }else{
  7.                 ... ...
  8.         }
复制代码
要包含头文件:
  1. #include <linux/sockios.h>
复制代码
查询接收缓冲使用:
FIONREAD来替代SIOCOUTQ

再次感谢,也供后来者参考。
作者: samzc2010    时间: 2011-10-20 17:57
我感觉lz你对这个问题理解出现了偏差。TCP协议是可靠传输协议,你投递到SOCKET端口后,自然有TCP协议栈的重传机制保证传输成功,然后会通知SOCKET端口数据传输状态,也就是你需要通过对SOCKET发送后的状态或者其他辅助函数检查数据是否成功,或者到底成功传输出去多少。不是你认为的那样,投递出去我就不管了,也不知到底有否成功了。这个和TCP协议无关,协议自然有他的保证机制,但是面向编程SOCKET它有反馈机制。建议你再仔细研究下异步SOCKET编程。不知道你使用的是什么模型发送数据。
作者: yulihua49    时间: 2011-10-20 19:57
tcp协议是可靠的,socket只是实现的一种具体方式,并不代表我们使用socket编写的应用程序一定是传输可靠的。 ...
notsureit 发表于 2011-10-20 11:21



    最终应该回一个包,表示收到了哪些。
作者: Redshadows    时间: 2011-10-20 20:40
我感觉lz你对这个问题理解出现了偏差。TCP协议是可靠传输协议,你投递到SOCKET端口后,自然有TCP协议栈的重 ...
samzc2010 发表于 2011-10-20 17:57

我想LZ的意思是——当应用进程调用send,并且此时发送缓冲区中有足够大地空间,那么send将把数据发送到该缓冲区中并成功返回。但是此后,可能因为网络中的某些原因致使TCP无法把数据发送到对端,此时应用进程的下层可能会返回消息告诉进程发送失败,但由于send在此之前已经成功返回,所以下层的消息无法正确地传递给应用进程,这就导致了LZ所说的send返回而TCP发送失败吧。不知道我这样理解对不对~~
我是个初学者,看不太懂LZ的解决方法,可否请高手解释一下~~~~
作者: notsureit    时间: 2011-10-21 09:55
回复 9# Redshadows


    就是你说的意思,我上面的解决方法就是在程序发现当前网络错误的时候,查询得到当前没有成功发送的位于socket发送缓冲区的数据字节数,由自己的应用程序做后续补发处理,因为后续的措施可能包括断开重连什么的,该缓冲区可能会丢失,为什么说是可能,而不是一定,也和关闭方式有关,不赘述了
作者: notsureit    时间: 2011-10-21 09:57
回复 8# yulihua49


    是这个意思,但是牵涉到服务端,这个代码不是我写的,协议也不是我定的,我只是在最小花费下负责收拾烂摊子
作者: notsureit    时间: 2011-10-21 10:00
回复 7# samzc2010


    你说的很对,tcp而不是udp,确实是负责这个的,但是注意,tcp不是socket,虽然几乎所有的tcp协议都使用socket来实现,他们仍然不是一回事,无论堵塞也好非堵塞也好,我们的写入操作本身的返回仅能代表放到了socket的发送缓冲,不确保已经成功送达,如果你写一个简单的程序来验证就会发现这一点。
    保证socket的发送缓冲区中数据不丢失,而不是应用程序自己的所谓发送缓冲,是我提出问题和解决问题的目的。

    如果我理解上有偏差,欢迎大家继续指证
作者: samzc2010    时间: 2011-10-21 10:09
个人觉得可能在这点上你的理解有点偏差。建议你在研究下异步SOCKET TCP方式的发送机制。不是这样的,不是说你投递到缓冲就结束了,TCP在数据的传送上有ACK机制保证可靠。这个ACK返回到你的应用程序接口上就是通知你缓冲内的数据已经发送了多少字节。通过这个状态检查你可以知道到底成功发送出去多少数据,还有多少数据未有发送,或者发送失败发生了。

个人意见,仅供参考。
回复  samzc2010


    你说的很对,tcp而不是udp,确实是负责这个的,但是注意,tcp不是socket,虽然几 ...
notsureit 发表于 2011-10-21 10:00

作者: zylthinking    时间: 2011-10-21 10:19
个人觉得可能在这点上你的理解有点偏差。建议你在研究下异步SOCKET TCP方式的发送机制。不是这样的,不是说 ...
samzc2010 发表于 2011-10-21 10:09


你先研究一下吧
作者: notsureit    时间: 2011-10-21 10:46
回复 13# samzc2010


   
这个。。。我只能说最初我也是像你这么想的,后来发现自己理解的不正确,建议你看看堵塞非堵塞的相关文章
作者: Redshadows    时间: 2011-10-21 11:09
那么,可以可否用select试试?这样数据发送失败时,上层应该可以接收到一个错误
作者: notsureit    时间: 2011-10-21 11:11
回复 17# Redshadows


    已经这么做了,还是上面提过的,如果缓冲区相对较大,网速相对较慢,那么select报错的时候,并不代表是当前发送出错,仍然需要我之前提到的问题和解决方法。
作者: Redshadows    时间: 2011-10-21 14:48
回复  Redshadows


    已经这么做了,还是上面提过的,如果缓冲区相对较大,网速相对较慢,那么selec ...
notsureit 发表于 2011-10-21 11:11

问一下,如果TCP发送多次失败并停止发送尝试时,TCP发送缓冲区中相应的数据应该会被丢弃掉吧?
作者: notsureit    时间: 2011-10-21 15:15
回复 19# Redshadows


    这个,我没有看底层代码,不好很确定的答复,但是在没有出现异常的情况下,这部分数据应该不会被丢弃,而应该会不停的被底层进行重试,因为数据本身是无差别的,在通道不存在物理错误的前提下,你不关闭重启,即使是丢弃了,谁又能保证可以就能发送成功那,所以还是应该根据具体的错误代码来做后续的处理。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2