Chinaunix

标题: linux下非阻塞的tcp研究 [打印本页]

作者: yulc    时间: 2007-07-30 15:40
标题: linux下非阻塞的tcp研究
这是根据自己的笔记整理的,如有错误,欢迎指出来.


tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据.

阻塞模式下,send函数的过程是将应用程序请求发送的数据拷贝到发送缓存中发送并得到确认后再返回.但由于发送缓存的存在,表现为:如果发送缓存大小比请求发送的大小要大,那么send函数立即返回,同时向网络中发送数据;否则,send向网络发送缓存中不能容纳的那部分数据,并等待对端确认后再返回(接收端只要将数据收到接收缓存中,就会确认,并不一定要等待应用程序调用recv);
非阻塞模式下,send函数的过程仅仅是将数据拷贝到协议栈的缓存区而已,如果缓存区可用空间不够,则尽能力的拷贝,返回成功拷贝的大小;如缓存区可用空间为0,则返回-1,同时设置errno为EAGAIN.



linux下可用sysctl -a | grep net.ipv4.tcp_wmem查看系统默认的发送缓存大小:
net.ipv4.tcp_wmem = 4096 16384 81920
这有三个值,第一个值是socket的发送缓存区分配的最少字节数,第二个值是默认值(该值会被net.core.wmem_default覆盖),缓存区在系统负载不重的情况下可以增长到这个值,第三个值是发送缓存区空间的最大字节数(该值会被net.core.wmem_max覆盖).
根据实际测试,如果手工更改了net.ipv4.tcp_wmem的值,则会按更改的值来运行,否则在默认情况下,协议栈通常是按net.core.wmem_default和net.core.wmem_max的值来分配内存的.

应用程序应该根据应用的特性在程序中更改发送缓存大小:




socklen_t sendbuflen = 0;
socklen_t len = sizeof(sendbuflen);
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);
printf("default,sendbuf:%d\n", sendbuflen);

sendbuflen = 10240;
setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, len);
getsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, &len);
printf("now,sendbuf:%d\n", sendbuflen);



需要注意的是,虽然将发送缓存设置成了10k,但实际上,协议栈会将其扩大1倍,设为20k.


-------------------实例分析----------------------


在实际应用中,如果发送端是非阻塞发送,由于网络的阻塞或者接收端处理过慢,通常出现的情况是,发送应用程序看起来发送了10k的数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中(未发送或者未得到接收端的确认).那么此时,接收应用程序能够收到的数据为2k.假如接收应用程序调用recv函数获取了1k的数据在处理,在这个瞬间,发生了以下情况之一:

A. 发送应用程序认为send完了10k数据,关闭了socket:
发送主机作为tcp的主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack),并且,发送缓存中的8k数据并不清除,依然会发送给对端.如果接收应用程序依然在recv,那么它会收到余下的8k数据(这个前题是,接收端会在发送端FIN_WAIT1状态超时前收到余下的8k数据.),然后得到一个对端socket被关闭的消息(recv返回0).这时,应该进行关闭.

B. 发送应用程序再次调用send发送8k的数据:
假如发送缓存的空间为20k,那么发送缓存可用空间为20-8=12k,大于请求发送的8k,所以send函数将数据做拷贝后,并立即返回8192;

假如发送缓存的空间为12k,那么此时发送缓存可用空间还有12-8=4k,send()会返回4096,应用程序发现返回的值小于请求发送的大小值后,可以认为缓存区已满,这时必须阻塞(或通过select等待下一次socket可写的信号),如果应用程序不理会,立即再次调用send,那么会得到-1的值,在linux下表现为errno=EAGAIN.


C. 接收应用程序在处理完1k数据后,关闭了socket:
接收主机作为主动关闭者,连接将处于FIN_WAIT1的半关闭状态(等待对方的ack).然后,发送应用程序会收到socket可读的信号(通常是select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);

如果发送应用程序没有处理这个可读的信号,而是继续调用send,那么第一次会像往常一样继续填充缓存区,然后返回,但如果再次调用send,进程会收到SIGPIPE信号,该信号的默认响应动作是退出进程.


D. 交换机或路由器的网络断开:
接收应用程序在处理完已收到的1k数据后,会继续从缓存区读取余下的1k数据,然后就表现为无数据可读的现象,这种情况需要应用程序来处理超时.一般做法是设定一个select等待的最大时间,如果超出这个时间依然没有数据可读,则认为socket已不可用.

发送应用程序会不断的将余下的数据发送到网络上,但始终得不到确认,所以缓存区的可用空间持续为0,这种情况也需要应用程序来处理.

如果不由应用程序来处理这种情况超时的情况,也可以通过tcp协议本身来处理,具体可以查看sysctl项中的:
net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalive_probes
net.ipv4.tcp_keepalive_time




所以,要想编写优秀的socket程序也是很不容易的.特别是在为应用做优化时,很多工作都非常的烦琐.


[ 本帖最后由 yulc 于 2007-7-31 13:34 编辑 ]
作者: linux_ha    时间: 2007-07-30 15:42
好贴!!!!!!!!!!!
作者: yulc    时间: 2007-07-30 22:07
好像并不太受关注
不知道是在应用中不需要了解这些细节,还是我没有讲明白我所理解的东西..

还是谢谢版主了,我的第一精...
作者: converse    时间: 2007-07-30 22:13
现在我写TCP server都是用的非阻塞模式的了。
作者: cookis    时间: 2007-07-31 09:45
绝世好贴...
有一点不明白...
,发送应用程序会收到socket可读的信号(通常是select调用返回socket可读),但在读取时会发现recv函数返回0,这时应该调用close函数来关闭socket(发送给对方ack);

发送方也要用select吗..那岂不是每一次发送前都要用select轮循一下..我一般就是send 返回 -1就表示对方关闭(或网络中断)
作者: cookis    时间: 2007-07-31 09:47
还是谢谢版主了,我的第一精...


第一精!! 哈哈...破了处了..
作者: flw    时间: 2007-07-31 09:51
【在阻塞模式下,send函数的过程是将应用程序请求发送的数据通过socket尽能力发送到对方接收缓存中(并非对方应用程),并返回成功发送的大小;】

这句话是错的吧?
我认为即使是在阻塞方式下,只要发送缓冲区还有空间,也仅仅只是拷贝而已。
而当缓冲区满了的时候,也仅仅只是坚持到缓冲区腾出足够的空间而已。
因此这句话应改为:

在阻塞模式下,send函数的过程是将应用程序请求发送的数据尽最大能力填充到己方的发送缓冲中(并非对方的接受缓冲),并返回成功放置到发送缓冲区中的大小。
作者: cookis    时间: 2007-07-31 09:55
对于视频数据传输大家一般都把接收/发送缓冲区设为多大 (TCP & UDP ) ??
作者: flw    时间: 2007-07-31 09:59
【如果发送应用程序没有处理这个可读的信号,而是继续调用send,那么第一次会像往常一样继续填充缓存区,然后返回,但如果再次调用send,进程会收到SIGPIPE信号,该信号的默认响应动作是退出进程.】

这个也不对。
我认为这个不是第一次还是第二次的问题,
而取决于发送端是否已经收到了对方的 FIN 包,
如果收到了就会 SIGPIPE 或者 EPIPE,
否则正常发送,不会出错。
作者: 思一克    时间: 2007-07-31 10:19
send 不仅仅是将内容放到自己buffer, 放后还要立即启动设备将包发出,即使buffer是相当空的。

原帖由 flw 于 2007-7-31 09:51 发表
【在阻塞模式下,send函数的过程是将应用程序请求发送的数据通过socket尽能力发送到对方接收缓存中(并非对方应用程),并返回成功发送的大小;】

这句话是错的吧?
我认为即使是在阻塞方式下,只要发送缓冲区还 ...

作者: flw    时间: 2007-07-31 10:22
原帖由 思一克 于 2007-7-31 10:19 发表
send 不仅仅是将内容放到自己buffer, 放后还要立即启动设备将包发出,即使buffer是相当空的。

但那时 write/send 已经返回了。
它并不会等待发送结果的。
发送是 TCP 软件的功能,
write/send 只否则交付,
并不关心结果。
作者: flw    时间: 2007-07-31 10:23
其实 write 系统调用和 TCP 软件之间是一个典型的“生产者-消费者”问题,
否则也不会有那个“缓冲区”了。
作者: 思一克    时间: 2007-07-31 10:43
send后,KERENL回立即启动dev发送.
write/send不会等待数据是否成功到达对方。
即使返回成功也不代表数据成功到达。
但返回错误却代表数据没有到达。



原帖由 flw 于 2007-7-31 10:22 发表

但那时 write/send 已经返回了。
它并不会等待发送结果的。
发送是 TCP 软件的功能,
write/send 只否则交付,
并不关心结果。

作者: flw    时间: 2007-07-31 10:46
原帖由 思一克 于 2007-7-31 10:43 发表
send后,KERENL回立即启动dev发送.
write/send不会等待数据是否成功到达对方。
即使返回成功也不代表数据成功到达。
但返回错误却代表数据没有到达。

那不就得了嘛。
作者: 思一克    时间: 2007-07-31 10:53
但是send/write不是“仅仅负责COPY到本地buffer"其它就不管了(就交给TCP了),
send/write(网络)的内核代码多部分也属于TCP本身。
它们要启动实际发送,还要尽最大努力检查网络连接的状态,返回许多网络的信息给调用者。

原帖由 flw 于 2007-7-31 10:46 发表

那不就得了嘛。

作者: flw    时间: 2007-07-31 10:59
原帖由 思一克 于 2007-7-31 10:53 发表
但是send/write不是“仅仅负责COPY到本地buffer"其它就不管了(就交给TCP了),
send/write(网络)的内核代码多部分也属于TCP本身。
它们要启动实际发送,还要尽最大努力检查网络连接的状态,返回许多网络的 ...

它只是唤醒了 TCP 软件而已。
作者: flw    时间: 2007-07-31 11:01
最关键的是,send/write 的所有动作都只在本地完成,并没有牵扯到网络操作。
作者: cookis    时间: 2007-07-31 11:02
我在一边瞅着呢...
作者: 醉卧水云间    时间: 2007-07-31 11:03
建议flw还是去补习一下网络编程吧,阻塞模式下,必须发到对端函数才返回。
作者: 醉卧水云间    时间: 2007-07-31 11:05
原帖由 cookis 于 2007-7-31 09:47 发表


第一精!! 哈哈...破了处了..



“设”完是不是很爽?
作者: flw    时间: 2007-07-31 11:06
原帖由 醉卧水云间 于 2007-7-31 11:03 发表
建议flw还是去补习一下网络编程吧,阻塞模式下,必须发到对端函数才返回。

请你自重。
你应该知道我非常不喜欢你。
你在 BSD 版撒泼让你失去了最后一次让我重新审视你的机会。
作者: 思一克    时间: 2007-07-31 11:11
send 本身在内核部分的代码就是TCP和网络程序的一部分。

看帖子:

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


本机发出的报文
(route if need)dev_queue_xmit |
ip_finish_output2 |<POSTROUTING> ip_finish_output | ip_output | ip_dst_output | <LOCALOUT> ip_push_pending_frames | raw_sendmsg | inet_sendmsg | sock_sendmsg | sys_sendmsg | sys_socketcall | sysenter_past_esp

调用启动和网络的许多函数。

也就是说socket write /send 和网络是在一起的,而没有一个清晰的buffer, send/write放入,网络程序取出那样的清晰的生产消费过程(虽然可以在一定程度上理解为生产/消费)
作者: cookis    时间: 2007-07-31 11:12
继续讨论 TCP ....不要受其他干扰
作者: 醉卧水云间    时间: 2007-07-31 11:16
原帖由 flw 于 2007-7-31 11:06 发表

请你自重。
你应该知道我非常不喜欢你。
你在 BSD 版撒泼让你失去了最后一次让我重新审视你的机会。


也请你自重,不知道不要乱说误导别人。

你也应该知道我非常不喜欢你,不要忘记是怎么卸掉本版斑竹的,你有生杀大权,我可以不要ID。

就凭你来审视我,笑话!你当你是老胡啊?想想你算老几再说这种居高临下的话吧,白写那么多代码,到头来连基本常识都不知道,真不知道你怎么写代码的,替你悲哀。
作者: yulc    时间: 2007-07-31 11:17
原帖由 cookis 于 2007-7-31 09:45 发表
绝世好贴...
有一点不明白...

发送方也要用select吗..那岂不是每一次发送前都要用select轮循一下..我一般就是send 返回 -1就表示对方关闭(或网络中断)






通常tcp应用是需要交互的,所以在编程实现中,如果采用非阻塞方式,一般是用select来检测socket是否可读/可写的.
你总不能在非阻塞的状态下这样写吧,如果你不怕累死CPU的话.
while(1)
{
  send();
  if(err != EAGAIN)
    break;
  eles
    continue;
}

所以,一般是写成这样:
while(1)
{
  select();
  if(可读)
    read();
  if(可写)
    recv();
  ...
}
作者: flw    时间: 2007-07-31 11:18
原帖由 思一克 于 2007-7-31 11:11 发表
本机发出的报文
(route if need)dev_queue_xmit |
ip_finish_output2 |<POSTROUTING> ip_finish_output | ip_output | ip_dst_output | <LOCALOUT> ip_push_pending_frames | raw_sendmsg | inet_sendmsg | sock_sendmsg | sys_sendmsg | sys_socketcall | sysenter_past_esp

具体到这里,就是实现相关了,Linux 的实现一向以糟糕著称,没准儿等 2.8 又会变成什么样子,而且,对于应用层程序员来说,他看不到这些。
总而言之,从逻辑上来看,send/write 的作用,就是把数据放入发送缓冲区,然后返回。至于它如何通知 TCP 来发送,以及 TCP 又如何发送,发送结果是什么,都不影响 send/write 的语义。
作者: yulc    时间: 2007-07-31 11:18
原帖由 flw 于 2007-7-31 09:51 发表
【在阻塞模式下,send函数的过程是将应用程序请求发送的数据通过socket尽能力发送到对方接收缓存中(并非对方应用程),并返回成功发送的大小;】

这句话是错的吧?
我认为即使是在阻塞方式下,只要发送缓冲区还 ...




这个你理解得不对,在阻塞模式下,send要保证发出的数据得到对端的ack确认后才会返回,也就是说当send返回时,请求发送的数据至少已经到达对端的接收缓存中.
作者: yulc    时间: 2007-07-31 11:19
原帖由 flw 于 2007-7-31 09:59 发表
【如果发送应用程序没有处理这个可读的信号,而是继续调用send,那么第一次会像往常一样继续填充缓存区,然后返回,但如果再次调用send,进程会收到SIGPIPE信号,该信号的默认响应动作是退出进程.】

这个也不对。
我认为这个不是第一次还是第二次的问题,
而取决于发送端是否已经收到了对方的 FIN 包,
如果收到了就会 SIGPIPE 或者 EPIPE,
否则正常发送,不会出错。



我的测试结果是,当接收端主动关闭后,发送应用程序调用send,但在这个send之前,即使发送端已经收到了对方的FIN包,应用程序也不会得到SIGPIPE,而仅仅是返回-1的结果,同时,会将errno置为对端已关闭的那个宏(我不记得叫什么了),那么,如果应用程序傻傻的再调一次send,这时候,SIGPIPE产生了.
作者: flw    时间: 2007-07-31 11:21
原帖由 醉卧水云间 于 2007-7-31 11:16 发表

也请你自重,不知道不要乱说误导别人。

你也应该知道我非常不喜欢你,不要忘记是怎么卸掉本版斑竹的,你有生杀大权,我可以不要ID。

就凭你来审视我,笑话!你当你是老胡啊?想想你算老几再说这种居高 ...

你自我感觉也太良好了吧?
以为我卸任本版版主是因为你逼的?
你不觉得你的声音在本帖里面很不和谐麽?

PS:我已经将你的回复加入 blacklist,请不要再向我回复,回了我也看不见。
作者: flw    时间: 2007-07-31 11:21
原帖由 yulc 于 2007-7-31 11:19 发表

我的测试结果是,当接收端主动关闭后,发送应用程序调用send,但在这个send之前,即使发送端已经收到了对方的FIN包,应用程序也不会得到SIGPIPE,而仅仅是返回-1的结果,同时,会将errno置为对端已关闭的那个宏(我 ...

哦?把你的测试代码发上来。
作者: 思一克    时间: 2007-07-31 11:24
好象不是你说的这样。

你可以实验,写一个阻塞的TCP,先send 1 个字节(1次),成功后将NIC 拔掉,然后再SEND 1个字节(2次), 然后再1个字节(3次)

2次可以返回成功的。3次失败。

该问题同原来争吵的feof问题道理一样。

原帖由 醉卧水云间 于 2007-7-31 11:03 发表
建议flw还是去补习一下网络编程吧,阻塞模式下,必须发到对端函数才返回。

作者: 醉卧水云间    时间: 2007-07-31 11:48
原帖由 思一克 于 2007-7-31 11:24 发表
好象不是你说的这样。

你可以实验,写一个阻塞的TCP,先send 1 个字节(1次),成功后将NIC 拔掉,然后再SEND 1个字节(2次), 然后再1个字节(3次)

2次可以返回成功的。3次失败。

该问题同原来争吵 ...


你这是欺骗系统的方式,拔掉网线后系统知道被拔是需要时间的。你可以等一段时间再发送试试,结果应不同。

还有就是系统因为效率上的原因,没有严格按send语意来做,send语意就是返回成功就是保证发送成功,但处理起来象你这样1个字节就发去确认没有效率,考虑到效率问题,发一个字节可能不会立刻发出等待确认,他要等一段时间或者超时,或者缓冲满再发出,然后等对方确认函数返回。

如果你每次发出的包足够长,则当时就可以确认网络断了,因为足够长的包会立刻发出并等对方确认。

你举的这个例子不可以用来反驳send仅仅是发到缓冲然后立刻返回,系统只是出于效率上的考虑才对这种小包进行特别对待,正常的包还是要等反馈再返回的。
作者: dzbjet    时间: 2007-07-31 11:52
越说越不明白了,呵呵。
不要人身攻击,无任何意义。
解决方式:
1. 看linux中的相关代码
2. 发送信件,咨询网络部分的linux kernel维护人员,不过回复的希望渺茫。
3. 大家讨论。

[ 本帖最后由 dzbjet 于 2007-7-31 11:54 编辑 ]
作者: 思一克    时间: 2007-07-31 11:53
send 可能要将一个字节经过很多设备发送到地球那一边。而且有时候网络又很慢。不可能等完成。再说了,只要对方不发确认,你如何知道完成了? 发了确认都未必见得是真的。

所以拔网络线后第2次可以成功返回没有什么问题。设计的就是这样的。而通过第2次,才有第3次的失败。

原帖由 醉卧水云间 于 2007-7-31 11:48 发表


你这是欺骗系统的方式,拔掉网线后系统知道被拔是需要时间的。你可以等一段时间再发送试试,结果应不同。

还有就是系统因为效率上的原因,没有严格按send语意来做,send语意就是返回成功就是保证发送成功 ...

作者: 思一克    时间: 2007-07-31 11:58
man send.

RETURN VALUE
       On  success,  these  calls return the number of characters sent.  On error, -1 is
       returned, and errno is set appropriately.

成功代表内容被成功地传输到网络了,不代表到达远方目的地。

还有看以下如此多的错误返回,也可以知道send绝对不是将内容放到本地buffer就返回(只要buffer有空间就成功返回)那样简单。


ERRORS
       These are some standard errors generated by the socket layer.  Additional  errors
       may  be  generated  and  returned from the underlying protocol modules; see their
       respective manual pages.

       EACCES (For Unix domain sockets, which are identified by pathname) Write  permisâ
作者: cookis    时间: 2007-07-31 12:20
各位大师..讨论之余能否给一些经验值...在视频传输应用中,非阻塞的情况下sendbuf_len or recvbuf_len 在TCP UDP下通常设为多大
作者: icydancer    时间: 2007-07-31 12:43
原帖由 思一克 于 2007-7-31 11:58 发表
man send.

RETURN VALUE
       On  success,  these  calls return the number of characters sent.  On error, -1 is
       returned, and errno is set appropriately.

成功代表内容被成功地传输到 ...

对,在阻塞模式下是这样的,send将数据发到网络上就ok了,不必等ack。而且一般tcp的实现是2次一个ack,所以要马上收到ack是不可能的。
非阻塞的时候好像就是拷贝到缓冲区。
还有一个证明,就是有个SO_SNDBUG,设为0的时候。数据应该会这接发出去吧。
作者: 醉卧水云间    时间: 2007-07-31 12:45
从原理上,发送放无法知道本次反送内容是否被对方收到,因为网络太复杂了,太遥远了,谁知道包正走着呢前方海底光缆就地震断了?

所以,TCP的上层应用协议都要自己确认,比如SMTP的250,220,等返回。POP3的+OK, +ERR。
FTP在应用层的验证确认估计更严格。

还有,我的拔NIC的例子,你可以将包变为128字节。只要有一个反例,就可以证明全部了。


原帖由 思一克 于 2007-7-31 11:58 发表
man send.

RETURN VALUE
       On  success,  these  calls return the number of characters sent.  On error, -1 is
       returned, and errno is set appropriately.

成功代表内容被成功地传输到 ...



当你发送的东西超过缓冲,则你的send返回后必定已被对方确认收到,但不是全部,是缓冲装不下的那部分字节或者更多的字节,send返回10000,可能被确认接受的是5000,总之超过缓冲区限制的字节必定有一些包需要对方确认,但send返回的值并不是可依赖的值,这里面主要还是缓冲的原因,如果缓冲为0,则发多少就确认多少。如果你发一个大数据包,则等对方的确认就需要很长的过程,你工作线程等在那里,是因为等待对方确认,所以是长过程。

非阻塞下,等的不是你的工作线程,是系统在等,完成后告诉你可以继续发。

man send中说发送成功不代表对方收到也是有道理的,因为有缓冲在中间,缓冲能存下的这部分不能确保对方收到,所以它就模糊的说你发的所有数据我不保证对方收到,实际是超过缓冲的那部分是肯定会被收到的,因效率问题,缓冲的部分变的不那么确定,这只是技术上的实现问题。

[ 本帖最后由 思一克 于 2007-7-31 12:54 编辑 ]
作者: 福瑞哈哥    时间: 2007-07-31 12:49
在阻塞模式下,如果发送包大于缓冲区长度,那中间肯定有一次要把发送端缓冲区的数据通过网络传送到接受端缓冲区。如果这时网络或者对端有问题,send会失败。

还有在发送端已知对方关闭或连接断掉的情况,send肯定会返回失败。

但是重点在于发送包长度小于发送缓冲区长度,而且发送端还不知对端情况时会怎么办?我也按照思一克的实验做了一下,证明了(至少是某些系统)send并不会真正触发网络传送。条件是小于缓冲区长度。
作者: yulc    时间: 2007-07-31 12:49
原帖由 flw 于 2007-7-31 11:21 发表

哦?把你的测试代码发上来。



哦,刚刚测了下,我说得不太对.
在接收端主动关闭后,如果发送端继续send,第一次会依然填充本地的发送缓存,如果缓存不够,才会返回-1,如果够,则正确返回;
但接下来,发送端应该是继续等待下一次可读或可写的信号,并且会收到可读的信号,然后recv时会发现返回0,
我说的是如果发送端没处理这个信号,再次send,那么就出错了,得到SIGPIPE.

我是临时在我的项目工程中测的,代码不太好贴...
测试步骤是:
1. A和B建立tcp,将A设为非阻塞方式;
2. A调用send发送1k数据给B,B也进行了recv;
3. 然后在A在第二次send时打上断点,停住;
4. 将B程序退出;
5. A继续第二次调用send,返回正确;
6. A第三次调用send,进程收到SIGPIPE,退出.
作者: yulc    时间: 2007-07-31 12:50
原帖由 cookis 于 2007-7-31 12:20 发表
各位大师..讨论之余能否给一些经验值...在视频传输应用中,非阻塞的情况下sendbuf_len or recvbuf_len 在TCP UDP下通常设为多大



这个要看你的网络情况以及服务器的负载情况而定.
你用默认的数值试试,如果哪方面不满意,再去做优化.
作者: 醉卧水云间    时间: 2007-07-31 13:00
原帖由 福瑞哈哥 于 2007-7-31 12:49 发表
在阻塞模式下,如果发送包大于缓冲区长度,那中间肯定有一次要把发送端缓冲区的数据通过网络传送到接受端缓冲区。如果这时网络或者对端有问题,send会失败。

还有在发送端已知对方关闭或连接断掉的情况,sen ...



把缓冲设置为0所有send都是被对方确认的。因为有缓冲,所以才出现这种情况。
作者: 福瑞哈哥    时间: 2007-07-31 13:04
原帖由 醉卧水云间 于 2007-7-31 13:00 发表



把缓冲设置为0所有send都是被对方确认的。因为有缓冲,所以才出现这种情况。

是的,你把缓冲设置成比发送包大就不用等待确认了。
作者: 醉卧水云间    时间: 2007-07-31 13:14
所来说去,buffer在这里起重要作用。

阻塞send本意是要发到对方确认的,但唯独buffer能容纳的那部分它不会被立刻发出确认。但它的主意很显然是一个需要对方确认的发送。
作者: 醉卧水云间    时间: 2007-07-31 13:16
原帖由 福瑞哈哥 于 2007-7-31 13:04 发表

是的,你把缓冲设置成比发送包大就不用等待确认了。


是已经确认了,但刨掉还在buf内的那部分,有时可能会全发出去,有时可能会留下部分直接返回全部字节。
作者: 醉卧水云间    时间: 2007-07-31 13:19
原帖由 思一克 于 2007-7-31 10:43 发表
send后,KERENL回立即启动dev发送.
write/send不会等待数据是否成功到达对方。
即使返回成功也不代表数据成功到达。
但返回错误却代表数据没有到达。





还是要等的,特例是buf能放下这些字节。
作者: 福瑞哈哥    时间: 2007-07-31 13:19
原帖由 醉卧水云间 于 2007-7-31 13:14 发表
所来说去,buffer在这里起重要作用。

阻塞send本意是要发到对方确认的,但唯独buffer能容纳的那部分它不会被立刻发出确认。但它的主意很显然是一个需要对方确认的发送。


还不如把阻塞send理解为「尽量发送全部数据,如果缓冲区满就先想办法清空缓冲区」更好一些。
作者: 醉卧水云间    时间: 2007-07-31 13:27
原帖由 福瑞哈哥 于 2007-7-31 13:19 发表


还不如把阻塞send理解为「尽量发送全部数据,如果缓冲区满就先想办法清空缓冲区」更好一些。


我觉得这样更好一些:

阻塞send是可靠的发送,但为了发送的效率,最后一部分buf能容纳的数据不会要求对方确认,而是假设这部分包将来会成功发送到对方,因为短期内我们预期网络连接不会存在问题,这部分包将少后0.x秒发送出去,因为有这部分数据的存在,我们建议你不要信任send返回值,如果你依赖于这个返回值,比如发完数据检查返回正常立即关闭连接,则把发送缓冲设置为0会更稳妥。
作者: yulc    时间: 2007-07-31 13:31
原帖由 醉卧水云间 于 2007-7-31 13:27 发表 我觉得这样更好一些:

阻塞send是可靠的发送,但为了发送的效率,最后一部分buf能容纳的数据不会要求对方确认,而是假设这部分包将来会成功发送到对方,因为短期内我们预期网络连接不会存在问题,这部分包将少后0.x秒发送出去,因为有这部分数据的存在,我们建议你不要信任send返回值,如果你依赖于这个返回值,比如发完数据检查返回正常立即关闭连接,则把发送缓冲设置为0会更稳妥。



概括得比我要好..
我原来的理解是错的,我更改一下贴子.
作者: 福瑞哈哥    时间: 2007-07-31 13:31
原帖由 醉卧水云间 于 2007-7-31 13:27 发表


我觉得这样更好一些:

阻塞send是可靠的发送,但为了发送的效率,最后一部分buf能容纳的数据不会要求对方确认,而是假设这部分包将来会成功发送到对方,因为短期内我们预期网络连接不会存在问题,这部分 ...


如果要确认对方收到,还是前面朋友说的在应用层返回确认消息比较好。
作者: 思一克    时间: 2007-07-31 13:55
你们说的发送缓冲区是setsockopt 的 SO_SNDBUF吗?




原帖由 醉卧水云间 于 2007-7-31 13:27 发表


我觉得这样更好一些:

阻塞send是可靠的发送,但为了发送的效率,最后一部分buf能容纳的数据不会要求对方确认,而是假设这部分包将来会成功发送到对方,因为短期内我们预期网络连接不会存在问题,这部分 ...

作者: 思一克    时间: 2007-07-31 14:17
TCP阻塞send是可靠发送,意思是如果连续send成功返回,那么本次send之前的是可靠发送了的。而刚才(本次)的还是不知道。

拔NIC电缆的实验已经证明了。

还有KENREL中其实没有一个专门为了缓冲发送内容的的很大的buffer(比如8192)。有的是skb在内核中的数据。其目的也不是为了缓存,而是为了实现网络功能的数据结构------没有它不可以。如果是缓存,那么就可以没有(比如文件系统的缓存,DIRECT-IO就可以使其根本不起作用)。但skb不行。

还有SO_SNDBUF最小2048,不可能为0。因为它本来不是做cache/buffer的意义。




原帖由 醉卧水云间 于 2007-7-31 13:27 发表


我觉得这样更好一些:

阻塞send是可靠的发送,但为了发送的效率,最后一部分buf能容纳的数据不会要求对方确认,而是假设这部分包将来会成功发送到对方,因为短期内我们预期网络连接不会存在问题,这部分 ...

作者: 福瑞哈哥    时间: 2007-07-31 14:30
原帖由 思一克 于 2007-7-31 14:17 发表
TCP阻塞send是可靠发送,意思是如果连续send成功返回,那么本次send之前的是可靠发送了的。而刚才(本次)的还是不知道。

事实上,本次之前send也不能保证就是可靠发送了的。
作者: 思一克    时间: 2007-07-31 14:35
也有可能
你实验看,拿出验证方法。

原帖由 福瑞哈哥 于 2007-7-31 14:30 发表

事实上,本次之前send也不能保证就是可靠发送了的。

作者: 福瑞哈哥    时间: 2007-07-31 14:38
原帖由 思一克 于 2007-7-31 14:35 发表
也有可能
你实验看,拿出验证方法。


在我的系统上,先连接上服务端。然后断掉网线,接着连续发送两次4K的数据,两次都是成功的返回4K。
作者: icydancer    时间: 2007-07-31 14:42
标题: 回复 #52 思一克 的帖子
哦?没有buffer?只有sk_buff?不解!
1.skb的确是个管理结构,但是它也有指向数据buffer的指针
2.buffer是的确存在的。要不tcp窗口管理的是什么?
3.据我所知,每个skb结构只管理一个包。(不考虑分片)跟buffer没什么关系吧。
希望您解释下,谢谢 (别说让我看内核源码去)
作者: 思一克    时间: 2007-07-31 14:48
我是说没有专门为了缓存(提高速度)的buffer. sk_buf是个管理结构,数据必须在其中。和FILE/IO的buffer是不同的。

比如skb的buffer你必须要,否则网络不会工作的。
而专门为了缓存设计的buffer是可以不要的---如果不想缓存的话。

你的理解是对的。skb有buffer的指针。指向的是数据的buffer.

原帖由 icydancer 于 2007-7-31 14:42 发表
哦?没有buffer?只有sk_buff?不解!
1.skb的确是个管理结构,但是它也有指向数据buffer的指针
2.buffer是的确存在的。要不tcp窗口管理的是什么?
3.据我所知,每个skb结构只管理一个包。(不考虑分片)跟buf ...

作者: 醉卧水云间    时间: 2007-07-31 14:53
原帖由 思一克 于 2007-7-31 14:17 发表
TCP阻塞send是可靠发送,意思是如果连续send成功返回,那么本次send之前的是可靠发送了的。而刚才(本次)的还是不知道。

拔NIC电缆的实验已经证明了。

还有KENREL中其实没有一个专门为了缓冲发送内容的的 ...


我记得win下可以设置缓冲为0,不过也不敢肯定,还真没这么设置过,能不能设置为0和系统实现有关,逻辑上没有一点问题,没有缓冲你就直接发应用程序里的数据嘛。如果linux不允许0缓冲,那可能是没考虑给用户这样设置的自由。
作者: 醉卧水云间    时间: 2007-07-31 15:03
原帖由 福瑞哈哥 于 2007-7-31 13:31 发表


如果要确认对方收到,还是前面朋友说的在应用层返回确认消息比较好。


常见的http就是发完关闭连接,再加一个应用层确认就烦琐了,简单的处理还是要依靠send返回值来确定是否发完数据。
作者: 福瑞哈哥    时间: 2007-07-31 15:12
原帖由 醉卧水云间 于 2007-7-31 15:03 发表


常见的http就是发完关闭连接,再加一个应用层确认就烦琐了,简单的处理还是要依靠send返回值来确定是否发完数据。


不确认对方是否收到不是什么大事,也不是设计错误,我自己就经常这么干。没问题。
作者: icydancer    时间: 2007-07-31 15:23
原帖由 醉卧水云间 于 2007-7-31 14:53 发表


我记得win下可以设置缓冲为0,不过也不敢肯定,还真没这么设置过,能不能设置为0和系统实现有关,逻辑上没有一点问题,没有缓冲你就直接发应用程序里的数据嘛。如果linux不允许0缓冲,那可能是没考虑给用户 ...

在windows不知道,linux2.4中可以设0,不过被内核修正了传进去的值。
val * 2 < SOCK_MIN_BUF。send buff 就设成SOCK_MIN_BUF.
作者: Sorehead    时间: 2007-07-31 15:41
我是比较同意flw的看法,在我看来,send、recv就是与缓冲区打交道,跟网络没有什么关系,真正的发送和接收是内核的事情,和应用程序无关。
至于阻塞还是非阻塞,出现的原因也不是为了得到接收端的确认,而是为了编写高效率的程序,io通常都是很慢的,非阻塞的作用就是让我们不用在等待io上浪费时间,而可以去做其它事情,例如操作其他io或者做一些逻辑处理。如果没有其他事情可做,那非阻塞也就失去意义了。
至于是否可靠,不是send的问题,而是事后接收协议回应包时再判断的问题。
作者: yulc    时间: 2007-07-31 15:49
原帖由 福瑞哈哥 于 2007-7-31 14:30 发表

事实上,本次之前send也不能保证就是可靠发送了的。



对的,如果拨掉对端网线的话,在本次send之前的几次send,也还在本地缓存中...
但数据实际上发到网络上去了,只是没有得到ack确认,所以缓存中并没有删除它们...
作者: linux_ha    时间: 2007-07-31 15:52
我正在看linux kernel network subsystem,看完了也许这贴我就有发言权了...
作者: 福瑞哈哥    时间: 2007-07-31 15:53
原帖由 yulc 于 2007-7-31 15:49 发表



对的,如果拨掉对端网线的话,在本次send之前的几次send,也还在本地缓存中...
但数据实际上发到网络上去了,只是没有得到ack确认,所以缓存中并没有删除它们...


对你说的「数据实际上发到网络上去了」持保留意见。
作者: yulc    时间: 2007-07-31 16:09
to 楼上,
我说的是拨掉对端的网线,这时候发送端是检测不到的,
所以肯定会将数据发到网络中,等待对端的确认.
作者: 福瑞哈哥    时间: 2007-07-31 16:14
原帖由 yulc 于 2007-7-31 16:09 发表
to 楼上,
我说的是拨掉对端的网线,这时候发送端是检测不到的,
所以肯定会将数据发到网络中,等待对端的确认.


问题是有可能根本就没有发送。
作者: yulc    时间: 2007-07-31 16:24
原帖由 福瑞哈哥 于 2007-7-31 16:14 发表


问题是有可能根本就没有发送。



不知道这个答复能不能说清楚.

我将192.168.0.229做为发送端, 192.168.0.226做为接收端,
当双方建立tcp后,将226主机的网线拨掉,在229上用tcpdump得出的结果是:

15:59:56.663510 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178712595 108716367>
15:59:56.663522 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1449:2897(1448) ack 1 win 1460 <nop,nop,timestamp 178712595 108716367>
15:59:56.866018 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178712646 108716367>
15:59:57.274042 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178712748 108716367>
15:59:58.090094 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178712952 108716367>
15:59:59.722196 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178713360 108716367>
16:00:02.986402 IP 192.168.0.229.35986 > 192.168.0.226.3400: . 1:1449(1448) ack 1 win 1460 <nop,nop,timestamp 178714176 108716367>


可以看出229首先向网络中发送了两个数据包,每个负载是1448bytes.
然后在200毫秒内,发现没有得到ack确认,再次发送了第一个数据包,然后经过大约400毫秒后再发了一次,
然后大约800毫秒后再发一次...
这个超时的值我认为取决于应用程序的设置,在linux默认应该是7200秒.
作者: icydancer    时间: 2007-07-31 16:29
标题: 回复 #68 yulc 的帖子
这个不能说明啊,因为现在想知道的是send返回时,是数据包发到网络的标志,还是送到buffer的标志。
上面的不能说明send返回时数据包已经发出还是在buffer里。(有可能是返回以后发的)

有个建议:利用上面抓到包的时间,和send返回的时间作下比较。
作者: 福瑞哈哥    时间: 2007-07-31 16:33
原帖由 yulc 于 2007-7-31 16:24 发表



不知道这个答复能不能说清楚.

我将192.168.0.229做为发送端, 192.168.0.226做为接收端,
当双方建立tcp后,将226主机的网线拨掉,在229上用tcpdump得出的结果是:

15:59:56.663510 IP 192.168.0.229.3 ...


兄弟,你的这个系统发送了数据不代表所有的系统都会在send时发送啊。

如果前面的结论是正确的话,就是说send只负责拷贝数据到缓冲区,内核tcp负责发送,那就有可能在send返回后没有发送啊。
作者: yulc    时间: 2007-07-31 16:43
标题: 回复 #69 icydancer 的帖子
"这个不能说明啊,因为现在想知道的是send返回时,是数据包发到网络的标志,还是送到buffer的标志。"
这个我认为要看当前发送端是什么状态,如果之前还有包没有得到确认,那么此次send肯定仅仅是送到buffer.


"上面的不能说明send返回时数据包已经发出还是在buffer里。(有可能是返回以后发的)"
这个有什么关系吗?
作者: baohuaihuai    时间: 2007-07-31 16:45
各位顺便讲解一下这个的过程(假设接收缓冲区和发送缓冲区都是8K,网络情况良好):

time1:发送端send 8K,接收端应用程序不recv
time2:接收端recv 2K.
time3:发送端send 8K.
time4:接收端recv 14K.
作者: yulc    时间: 2007-07-31 16:52
原帖由 福瑞哈哥 于 2007-7-31 16:33 发表


兄弟,你的这个系统发送了数据不代表所有的系统都会在send时发送啊。

如果前面的结论是正确的话,就是说send只负责拷贝数据到缓冲区,内核tcp负责发送,那就有可能在send返回后没有发送啊。



确实有可能,如果此时前面还有很多数据没有确认的情况下.
作者: icydancer    时间: 2007-07-31 16:52
原帖由 yulc 于 2007-7-31 16:43 发表
"这个不能说明啊,因为现在想知道的是send返回时,是数据包发到网络的标志,还是送到buffer的标志。"
这个我认为要看当前发送端是什么状态,如果之前还有包没有得到确认,那么此次send肯定仅仅是送到buffer.

...

有关系,现在大家想知道的是,send是将数据发到网络后再返回?还是仅仅将数据放到buffer后就马上返回?
你上面的数据不能说明这一点
作者: yulc    时间: 2007-07-31 16:54
原帖由 baohuaihuai 于 2007-7-31 16:45 发表
各位顺便讲解一下这个的过程(假设接收缓冲区和发送缓冲区都是8K,网络情况良好):

time1:发送端send 8K,接收端应用程序不recv
time2:接收端recv 2K.
time3:发送端send 8K.
time4:接收端recv 14K.



结果:
time1: 发送端将8k数据分为N个数据包发送到了接收端的缓存中,接收端等待应用程序recv;
time2: 接收端收到了2k,缓存中还有6k;
time3: 发送端又将8k数据分N个包到接收端的接收缓存中;
time4: 接收端得到了14k数据.
作者: icydancer    时间: 2007-07-31 16:55
原帖由 yulc 于 2007-7-31 16:52 发表



确实有可能,如果此时前面还有很多数据没有确认的情况下.

看来我的表达能力太差了 福瑞一说你就能明白

[ 本帖最后由 icydancer 于 2007-7-31 16:56 编辑 ]
作者: baohuaihuai    时间: 2007-07-31 16:56
原帖由 yulc 于 2007-7-31 16:54 发表



结果:
time1: 发送端将8k数据分为N个数据包发送到了接收端的缓存中,接收端等待应用程序recv;
time2: 接收端收到了2k,缓存中还有6k;
time3: 发送端又将8k数据分N个包到接收端的接收缓存中;
time4: 接收 ...


time3过后,发送缓冲区中有多少字节的数据?是8K?6K?
作者: yulc    时间: 2007-07-31 17:00
原帖由 icydancer 于 2007-7-31 16:52 发表

有关系,现在大家想知道的是,send是将数据发到网络后再返回?还是仅仅将数据放到buffer后就马上返回?
你上面的数据不能说明这一点



我认为这个是不可确定的..
作者: yulc    时间: 2007-07-31 17:01
原帖由 baohuaihuai 于 2007-7-31 16:56 发表


time3过后,发送缓冲区中有多少字节的数据?是8K?6K?



time3过后,发送缓存中为0,因为得到了接收端的ack了.
作者: yulc    时间: 2007-07-31 17:06
原帖由 icydancer 于 2007-7-31 16:55 发表

看来我的表达能力太差了 福瑞一说你就能明白



其实我在回复你贴子时也在自我进化..
很多时候,我在给别人解释一个东西时,发现原来自己也理解得不够透,或者在说着说着时瞬间理解透...
作者: baohuaihuai    时间: 2007-07-31 17:10
原帖由 yulc 于 2007-7-31 17:01 发表



time3过后,发送缓存中为0,因为得到了接收端的ack了.

可是接收缓存已经满了啊
作者: icydancer    时间: 2007-07-31 17:18
原帖由 yulc 于 2007-7-31 17:00 发表



我认为这个是不可确定的..


这个在linux系统还好说点,看内核里的send函数什么情况下返回。是将数据放到tcp的窗口buffer中,还是,直接调用tcp层的函数发送。
在windows里就没法证明了。

看有没有什么标准对send有规定。

ps:说道这我感觉flw说的可能是正确的,可能。。。
作者: 思一克    时间: 2007-07-31 17:37
这是一个send()的内核调用跟踪。


15826 "telnet" d590c034 {  S} | qdisc_restart | dev_queue_xmit | neigh_resolve_output | ip_finish_output | ip_output | ip_dst_output | ip_queue_xmit | tcp_transmit_skb | tcp_write_xmit | __tcp_push_pending_frames | tcp_sendmsg | inet_sendmsg | sock_sendmsg | sys_sendto | sys_send | sys_socketcall
作者: bluster    时间: 2007-07-31 17:40
flw应该是对的。。。
作者: benjiam    时间: 2007-07-31 17:41
好像没什么意义啊!

所谓的拔网线测试更是奇怪了。 没有意义。

在阻塞和非阻赛的时候 send 没什么区别的, 都是copy 栈里面。 就返回了。 没什么区别

关键在recv
作者: caijimin    时间: 2007-07-31 17:53
下面是从UNIX网络编程(卷I 第3版)2.11 TCP Output摘出来的一段

The kernel will not return from the write until the final byte in the application buffer has been copied into the socket send buffer. Therefore, the successful return from a write to a TCP socket only tells us that we can reuse our application buffer. It does not tell us that either the peer TCP has received the data or that the peer application has received the data.
作者: xiaxueyi    时间: 2007-07-31 17:57
标题: 回复 #1 yulc 的帖子
你们讨论得这么复杂,我问个简单的问题
在非阻塞的情况下,应用程序调用send发送数据后返回,在不与接收端再次通讯的情况下,如何知道内核有没有把发送端缓冲区的数据发出去?如果没有,如何知道错误原因?
作者: trulyliu    时间: 2007-07-31 21:20
TCP缓冲区防止阻塞的优化算法呢?
怎么没有提到?
作者: yulc    时间: 2007-07-31 21:45
原帖由 baohuaihuai 于 2007-7-31 17:10 发表

可是接收缓存已经满了啊



sorry,我不够仔细,我没有看到你所设的接收缓存是8k
这样的话,我想情况会是这样的:
由于第一次过后,缓存中还有2k的空间,所以会在ack包中告诉发送端我的滑动窗口大小为2048bytes,
这时,发送端会发一个约为1500(假设MTU为1500)大小的包过来,然后再是一个500字节的包.
这时候,如果接收应用程序还没有调用recv来获取缓存中的数据,
那么接收端会在ack中通知发送端滑动窗口已为0,需要它暂停发送.
作者: yulc    时间: 2007-07-31 21:59
原帖由 xiaxueyi 于 2007-7-31 17:57 发表
你们讨论得这么复杂,我问个简单的问题
在非阻塞的情况下,应用程序调用send发送数据后返回,在不与接收端再次通讯的情况下,如何知道内核有没有把发送端缓冲区的数据发出去?如果没有,如何知道错误原因?



在程序中我不知道如何得到,哪位知道,请告诉我.
我只会用netstat -a 查看, Send-Q 那列就是.
作者: flw    时间: 2007-07-31 22:25
思一克也是对的,
只是思一克过于强调内核实现,
我认为应用层的事就是应用层的事,
虽然本帖标题是 Linux 下的 TCP,
但是相信很多人都在非 Linux 平台比如 BSD、Unix 等平台下有工作任务,
即使是 Linux 内核本身,它也是在进化当中,目前不才 2.x 嘛。
因此我不赞成将内核实现牵扯到本帖的讨论中来。

福瑞哈哥的意见也是对的。
作者: feasword    时间: 2007-07-31 23:06
应用层对tcp说,老兄,发1000个字节数据
对于阻塞的socket,
tcp说,ok,有地方,给我吧,你走先,send返回
或者
tcp说,我这没地方了,你等等,我腾个地方出来,send阻塞,直到收到前面某个afk,能清出空间放这1000字节数据。
假如tcp见到点数据就让应用层等回复,那tcp的n个k的缓冲区,tcp所谓流的n多算法,还有存在的必要么?
作者: 思一克    时间: 2007-08-01 10:02
TCP的标准设计估计是帖子引用的那样。所以说send将数据COPY到buffer就返回了应该也没有问题。但是LINUX上的有些出入

我看了LINUX TCP代码,对于send ( tcp_sendmsg)有结论:
1)tcp_sendmsg 将数据COPY到buffer, 并不是(将发送任务交给TCP)立即返回。而是,根据情况判断,如果该发送了,自己启动网络设备发送,然后再返回。也就是说,TCP本身不是一个独立的消费者进程(象kblockd, aio那样,read/write路径将数据请求填充buffer, 然后就返回,等待它们读写磁盘完成), 我前面帖子给出的调用路径说明了这一点。----- 为什么要这么做?而不是如同“标准”说的那样?我分析主要是考虑效率。你想,USER APP send了100个字节,如果你将100个字节拷贝到buffer就返回,返回前势必通知TCP(KENREL THREAD,如果有的话)发送。这就有进程切换(代价比100个字节处理要浪费的多),自己直接做是最快的。
那为什么kblockd(磁盘)这样实现?磁盘是机械设备读写非常慢,有切换的代价也值得。而网卡不是机械的东西,将数据给网卡比给磁盘快的多。

2)tcp_sendmsg要对网络状态做许多检查,如果不能发送了,就给send许多关于网络的错误信息返回。

3)tcp_sendmsg只要填充了buffer(说明目前网络还没有什么问题),就会返回成功的字节数目。也即使说,无论启动网络设备发送的结果如何,对返回没有影响。所以send返回成功,根本不代表数据发送到对方了,仅仅代表到本地buffer了,或者已经送本地网络设备了。






原帖由 benjiam 于 2007-7-31 17:41 发表
好像没什么意义啊!

所谓的拔网线测试更是奇怪了。 没有意义。

在阻塞和非阻赛的时候 send 没什么区别的, 都是copy 栈里面。 就返回了。 没什么区别

关键在recv

[ 本帖最后由 思一克 于 2007-8-1 10:05 编辑 ]
作者: benjiam    时间: 2008-01-09 11:41
回一下,当时回的时候比较草率,抱歉。  其实还是有很多细节,我们并没有仔细检查过, 比如上次和我搭伙的一个java 代码因为网络处理有问题,我采用的是阻塞发生,结果出现了windows 为0 的情况。而我的线程就挂在send 上面。 其实这种非常细的问题太深入又点失去它的意义了。  一般在应用层面上都会有确认。 因为UNP 说的 即使对方真的recv 成功了,你也不能保证别人在处理的时候不崩溃。的确是这样的。
  但我认为send 争论的焦点在于, 阻塞成功了,是否会出现对方tcp 栈其实没有收到这种情况。因为在应用层协议不是很完善的情况下。很多时候,发送端靠发送成功作为某些流程的行进的一个标志。 但是目前看来 是会的。 概率很小,但是的确会. 某些特殊的情况下 会。
作者: bical    时间: 2008-10-05 23:27
mark
作者: marco_hxj    时间: 2009-10-13 20:16
标题: 回复 #7 flw 的帖子
delete by m

[ 本帖最后由 marco_hxj 于 2009-10-15 12:55 编辑 ]
作者: cskyrain    时间: 2009-11-04 11:40
问一下:有统一说法没
作者: xzh_endless    时间: 2009-11-05 00:17
提示: 作者被禁止或删除 内容自动屏蔽
作者: kkndmammoth    时间: 2009-11-05 12:38
好贴,mark.
作者: 酒饮微醉    时间: 2009-11-05 13:19
原帖由 flw 于 2007-7-31 09:51 发表
【在阻塞模式下,send函数的过程是将应用程序请求发送的数据通过socket尽能力发送到对方接收缓存中(并非对方应用程),并返回成功发送的大小;】

这句话是错的吧?
我认为即使是在阻塞方式下,只要发送缓冲区还 ...

支持!!




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