不知道是不是应该发在这里,看了一下这里好像主要是交流网络配置的?但其它更想不出什么地方了。内容比较多,请大家见谅。 在阻塞式的socket中,recv/read 调用肯定不一定返时刚好读满缓冲区(即使对端没有关闭还在发送),这个比较容易理解,也很容易测试出来。但对于send的返回值,许多资料中讲得都很含 糊,在MSDN中说是“非阻塞的socket有可能发送成功而返回的值小于要发送的长度“,即只发送了一部分出去,感觉言下之义就是阻塞式的只要发送成功 就肯定是指定的发送长度,和文件写一样。Linux/FreeBSD中man手册都没有明确的说明,只说成功时返回发送的数据字节数,失败返回-1。 以前刚学网络时看的是《UNIX网络编程》的第二版,现在俺这本书找不到了,于是找了一本第三版的电子书看,但浏览了一下没看到什么说明。这个发送实际上以前就比较困惑,由于自己不做网络开发也没有专门测试过,问过一些专门做网络的好像也不是很清楚;而接收如果按每次能收满这个在工作中是见到过问题的。 《UNIX网络编程》第三版的代码:
vsFTP中的代码:
后面一个实际上是写socket的。从这两种看感觉都是认为写操作有可能不能一次写完,而且这里显然不是处理非阻塞式的,因为没有处理非阻塞式特别的返回码。 然后我开始做实验。结果发现不管缓冲区多大,对端阻塞的话本端要么阻塞,要么全部发送。不管多大缓冲区发送,都无法构造出有可能发磅成功但只发送部分的情况。 如果是这样的话也算OK吧,因为Linux的man手册中讲到send/recv的第四个参数是WAITALL只提到读数据。但这样就又使我对于 select疑惑起来。因为select对读很简单,保证读一次不会阻塞是可以的。但对于这个测试结果,select可写的话后面的写操作有可能还是阻塞 掉,因为select时并不知道后续的发送要发多少数据。UNPv3给出的select返回socket可写的条件(其它三个条件不相关): The number of bytes of available space in the socket send buffer is greater than or equal to the current size of the low-water mark for the socket send buffer and either: (i) the socket is connected, or (ii) the socket does not require a connection (e.g., UDP). This means that if we set the socket to nonblocking (Chapter 16), a write operation will not block and will return a positive value (e.g., the number of bytes accepted by the transport layer). We can set this low-water mark using the SO_SNDLOWAT socket option. This low-water mark normally defaults to 2048 for TCP and UDP sockets. 最开始我以WinXP 做Server,连接之后等待键盘输入才读数据,这样Linux下的客户端最终总会阻塞,而在Server读几次之后客户端会再写一些数据。最后发24K 包发送时确实会出来select返回可写,但实际的send阻塞的情况,不过好像有一些随机。另外此时发现一个奇怪的现象,因为Windows下SOL_RCVBUF缺省为8K,而Linux下SOL_SNDBUF缺省为16K,但实际上发了许多24K包才阻塞,实在是想不明白,所以抓包看了一下,发现确实发给Windows的数据包远超过8K之后它才发Window Full,不知道除了TCP的接收缓冲区Windows还有哪也缓存数据了。 后来在FreeBSD下面编译客户端也测试了一下。在FreeBSD下发送缓冲区缺省是32K,但肯定地发完第5包之后select再交返回1,然后就阻 塞了。Linux下有些随机,往往是发完第6包阻塞在select上,后面随着Server读一些数据有可能select返回1但却发送阻塞,但有时也是 发完第5包之后select返回1后发送 阻塞。 另外比较奇怪的是在Linux下SOL_SNDLOWAT值为1,而FreeBSD是前面UNP中提到的2K,所以按道理Linux应该更容易出现select返回中写而发送阻塞,但测试结果FreeBSD更固定地出现。 环境是Fedora 7.0和FreeBSD 7.0。 由于是测试代码,有些乱。Client代码如果要在FreeBSD下编译需要在 #include <sys/types.h> #include <sys/socket.h> 后面再包含一些文件: #include <netinet/in.h>
|
|