Chinaunix

标题: write socket时的一个疑问 [打印本页]

作者: scutan    时间: 2007-12-22 15:03
标题: write socket时的一个疑问
想请教一下, 我有两台机器在通信, PC1, PC2, 当PC1要向PC2发送数据的时候, 我把PC2的网线断掉, 此时PC1在调用write()的时候, write()会成功返回, 并且返回写入内核的字节数, 但是PC1由于没有收到PC2的ACK包, 会一直重传, 并且在几分钟之后放弃发送数据.

此时如果做如下操作分别有如下对应的结果:

1. PC1继续write, 则write出错, 并且返回没有路由可到达的错误. 断掉socket
2. PC1在write之后就调用read, read会阻塞, 过一会儿read会返回和上面一样的错误,也是路由不可达的错误. 断掉socket.
3. PC1不再管这个socket了, 做其它的事情, 那么我观察了一下, 过了一会儿这个socket就断掉了. 对进程并没有什么影响.

这只是我实验的数据. 我看TCP/IP详解上面也只是讲了重传的算法, 并没有讲如何影响用户态程序的, UNP上也没有讲这种情况, 不知道各位以前对这个有没有什么研究吗?
那么平时编网络程序时应该如何来解决这种问题呢? 我现在也只是在应用层自己制定了一种协议, 即在write之后就调用read, 通过read的返回值来判断. 不知道各位还有没有什么其它的好方法?
望指教.
谢谢.
作者: 思一克    时间: 2007-12-22 15:03
TCP流可以保证快速.
你发了1K, 等待确认, 那流量就受大影响了.

设置超时, 不断发, 直到write出错或超过时间限制.
这样可以充分利用TCP流的速度.
网络大部分时间是通的,断的时候是少数.

例子,SMTP发送邮件就是如此. 你不能发1K,就等待回应, 确认后再发1K,... 那速度就十分慢了.
SMTP发邮件内容时候,就是不断发, 直到最后发完,或出错误,或TIMEOUT.



http://bbs.chinaunix.net/viewthr ... =%CB%BC%D2%BB%BF%CB

从北京到上海建立了集装箱货物运输线路.

北京是发送方,上海是接收方.
货物发送者将货物送到北京的货场. 运输公司将货物标记,装箱,然后配车往上海一箱一箱运.
货场的存货不多.

上海方面收到了箱,打电话告诉北京货场,某编号的箱已经收到了,并且打开箱根据标识重新分配货物,等货物接收者来取走.

如果运输路线桥断了,塞车了,

1)发送方不可能在第一时间知道,所以它还继续发. 一直塞到门口为止. 或者:
2)发送方一般知道一个箱子发车后最长多久一定可以收到上海的收到确认电话.比如一天.如果3天还没有,北京也可以确定路上出问题了.

这个类似于TCP STREAM SOCKET的工作过程.
有很多人问,为什么线路断了(比如网络线拔了)发送方还可以成功地send.
比喻不完全恰当,但说明道理.
作者: cugb_cat    时间: 2007-12-22 15:21
如果不显式的调用read write(或其他针对socket的操作函数)  应用程序应该不会得到提示
作者: scutan    时间: 2007-12-22 15:27
标题: 回复 #2 cugb_cat 的帖子
嗯. 我得到的也是这样的结果, 那是不是一般在write之后都应该有一定的检测措施呢. 如read, write.
我写代码的经验不太多, 所以有些遇到的问题还很陌生.
作者: converse    时间: 2007-12-22 15:28
一般得,我的server端在accept每个clinent时保存一个时间,并且配置一个参数是处理每个连接最长的时间,这个时间一般是十几秒不会超过一分钟(当然也按照你的业务处理是否复杂而定),server端采用的IO复用模型,每次轮询的时候都会去查询当前时间与最开始接收到连接的时间差是不是超过了最大的范围,如果是的话就关闭socket,否则就一直保存这个连接在server端,当send出错时也关闭这个socket。

[ 本帖最后由 converse 于 2007-12-22 15:32 编辑 ]
作者: cookis    时间: 2007-12-22 15:33
如果你是服务端的话..建议采用select 来轮循socket
如果是客户端的话..你发送了数据可以马上调用read 来进行同步地接收..也可以另起一个线程用 select来异步接收..

至于想判断socket 是否有效..你可以让客户端定时向服务端发跳消息..来维持这个连接..如果心跳超时,则认为失败.
作者: scutan    时间: 2007-12-22 15:51
标题: 回复 #4 converse 的帖子
那你每次轮循的时候是不是会消耗很多的时间啊, 因为你每个client都要去判断一下. 还是每次select返回都只是查看当前描述符的时间?
作者: cugb_cat    时间: 2007-12-22 15:55
这里就是一个TCP连接的保活的过程,我觉得在不是很严格的情况下,在没有调用read或write时可以不关注这个socket的有效性,当read write调用出错了,再进行重连。
作者: xhl    时间: 2007-12-22 20:35
原帖由 scutan 于 2007-12-22 15:03 发表
想请教一下, 我有两台机器在通信, PC1, PC2, 当PC1要向PC2发送数据的时候, 我把PC2的网线断掉, 此时PC1在调用write()的时候, write()会成功返回, 并且返回写入内核的字节数, 但是PC1由于没有收到PC2的ACK包, 会 ...


我没怎么看明白你的 问题, 给你点建议, 也许不是你的问题的 答案。

windows linux 还有部分 unix kernel已经提供了 tcp keepalive的 能力,

你只需要用 setsockopt 设置己个 arg就可以了  keepalive , keepidle, keepinternal .....

当条件满足的时候, kernel就会发 出一些probe tcp packet去帮你探测对方是否还活着。

如果对方死掉了, 会通知用户进程的。
作者: ardi    时间: 2007-12-22 20:53
标题: 回复 #1 scutan 的帖子
楼主的做法是对的,应该用协议来确保socket 通信的完整性
作者: 思一克    时间: 2007-12-22 21:50
你这是好方法吗?
不是。速度会慢许多许多。

"那么平时编网络程序时应该如何来解决这种问题呢? 我现在也只是在应用层自己制定了一种协议, 即在write之后就调用read, 通过read的返回值来判断. 不知道各位还有没有什么其它的好方法?
"
作者: cugb_cat    时间: 2007-12-22 23:31
原帖由 思一克 于 2007-12-22 21:50 发表
你这是好方法吗?
不是。速度会慢许多许多。

"那么平时编网络程序时应该如何来解决这种问题呢? 我现在也只是在应用层自己制定了一种协议, 即在write之后就调用read, 通过read的返回值来判断. 不知道各位还有 ...

版主给说一个好方法嘛,呵呵。
作者: scutan    时间: 2007-12-23 10:39
原帖由 xhl 于 2007-12-22 20:35 发表


我没怎么看明白你的 问题, 给你点建议, 也许不是你的问题的 答案。

windows linux 还有部分 unix kernel已经提供了 tcp keepalive的 能力,

你只需要用 setsockopt 设置己个 arg就可以了  keepali ...


谢谢, 我的意思其实就是说如果我在向另一台机器write的时候, 那台机器突然断网了, 所以此时的发送机收不到任何可以提示的信息, 如EOF, RST包之类的, 如果在我write之后我再调用 read 或者是 write的话, 则可以发现这个问题, 但是如果就不再调用 read , write之类的了, 我发送机就根本不能确认我之前那个write是否能够成功到达. 所以为了保证可靠性, 我一般都要再一次的read或者是write以确定一下.
我就是想请教一下, 大家在遇到这种问题的时候是如何处理的.
作者: scutan    时间: 2007-12-23 10:40
原帖由 思一克 于 2007-12-22 21:50 发表
你这是好方法吗?
不是。速度会慢许多许多。

"那么平时编网络程序时应该如何来解决这种问题呢? 我现在也只是在应用层自己制定了一种协议, 即在write之后就调用read, 通过read的返回值来判断. 不知道各位还有 ...


请版主指教, 我就是把我的想法说出来, 然后再希望得到大家更多的指导. 谢谢!
作者: converse    时间: 2007-12-23 13:46
标题: 回复 #6 scutan 的帖子
我最近在看lighttpd的代码,也是这样做的.
作者: scutan    时间: 2007-12-23 14:38
标题: 回复 #15 思一克 的帖子
嗯. 谢谢, 这种方法确实是要比我说的那种效率高.

>>直到最后发完,或出错误,或TIMEOUT.
当不断地发送完毕之后也应该会有一个recv之类的操作吧, 以确定是否所有的send都已经成功. 因为如果没有这一步仍然无法确定是否所有的都已成功.

>>2)发送方一般知道一个箱子发车后最长多久一定可以收到上海的收到确认电话.比如一天.如果3天还没有,北京也可以确定路上出问题了.
这一步, 也应该是使用recv来完成的吧. send完了之后没有办法知道是否成功.

总之, 我的看法是, 不管是一次send一次recv还是多次send, 之后总得有一个recv来实现这个可靠的通知.
谢谢指教.
作者: 思一克    时间: 2007-12-23 15:02
应用层有协议确然。

但不应该是每个write之后确认, 而是多个(几十,几百,数千个)以后确认。
比如你有10M的一个邮件要发,DATA后发送端就不断发了,接受端收到结束标志后,发一个OK类似的话告诉发送端。发送端10M发完后要收一下。



原帖由 scutan 于 2007-12-23 14:38 发表
嗯. 谢谢, 这种方法确实是要比我说的那种效率高.

>>直到最后发完,或出错误,或TIMEOUT.
当不断地发送完毕之后也应该会有一个recv之类的操作吧, 以确定是否所有的send都已经成功. 因为如果没有这一步仍然无法 ...

作者: scutan    时间: 2007-12-23 15:14
原帖由 思一克 于 2007-12-23 15:02 发表
应用层有协议确然。

但不应该是每个write之后确认, 而是多个(几十,几百,数千个)以后确认。
比如你有10M的一个邮件要发,DATA后发送端就不断发了,接受端收到结束标志后,发一个OK类似的话告诉发送端。 ...


明白了, 谢谢!
作者: 思一克    时间: 2007-12-23 15:48
不用。

原帖由 scutan 于 2007-12-23 15:14 发表


明白了, 谢谢!

作者: scutan    时间: 2007-12-23 15:50
谢谢前面各位的关注, 不过分只能给一位朋友, 以后有机会再补上.
作者: cookis    时间: 2007-12-24 11:39
有这么复杂吗..?

没必要在write 的时候先读一下吧..
况且你还得把预先把它设成非阻塞的..然后等write的时候再马上设回来

为什么不用心跳呢...客户端定时发送心跳 (可以根据你的业务需要调整心跳间隔) MSN QQ不都是这样吗.
每次select前都去检查一下每个client的心跳是否超时..当然如果你怕影响效率的话..可以单起一个线程去定期检查心跳嘛.心跳丢失.你再把 fd 删掉..




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