关于Recv的疑惑..........

75 views
Skip to first unread message

楠楠

unread,
Dec 11, 2006, 1:10:16 AM12/11/06
to 高性能网络编程邮件列表
客户端发了一个数据包, 带有包头和包体,
包头是定长,包体是变长的.
如果接收方分两次接收,第一次recv读包头个字节,第二次recv根据包头里的长度读变体内容.
却发现第二次没有数据了.
是不是因为第一次recv把数据全部接收了,但它只读出了包头的长度,包体被丢了?

如果两方都按固定的长度一次发送比如2000个字节,分多次发送,多次接收这个问题就不存在了.因为可能第一次已经把全部数据接收到缓冲区了,如果再分析包头,发现比2000还要长,就可以分批次接收,一直到数据全部接收完毕.

但看了下面的代码,我的疑问又来了:
#include "etcp.h"

int main( int argc, char **argv )
{
SOCKET s;
int n;
struct
{
u_int32_t reclen;
char buf[ 128 ];
} packet;

INIT();
s = tcp_client( argv[ 1 ], argv[ 2 ] );
while ( fgets( packet.buf, sizeof( packet.buf ), stdin )
!= NULL )
{
n = strlen( packet.buf );
packet.reclen = htonl( n );
if ( send( s, ( char * )&packet,
n + sizeof( packet.reclen ), 0 ) < 0 )
error( 1, errno, "send failure" ); //一次发送完了.
}
EXIT( 0 );
}

#include "etcp.h"

int main( int argc, char **argv )
{
struct sockaddr_in peer;
SOCKET s;
SOCKET s1;
int peerlen = sizeof( peer );
int n;
char buf[ 10 ];

INIT();
if ( argc == 2 )
s = tcp_server( NULL, argv[ 1 ] );
else
s = tcp_server( argv[ 1 ], argv[ 2 ] );
s1 = accept( s, ( struct sockaddr * )&peer, &peerlen );
if ( !isvalidsock( s1 ) )
error( 1, errno, "accept failed" );
for ( ;; )
{
n = readvrec( s1, buf, sizeof( buf ) );
if ( n < 0 )
error( 0, errno, "readvrec returned error" );
else if ( n == 0 )
error( 1, 0, "client disconnected\n" );
else
write( 1, buf, n );
}
EXIT( 0 ); /* not reached */
}


主要是readvrec函数, 请看如下:

int readvrec( SOCKET fd, char *bp, size_t len )
{
u_int32_t reclen;
int rc;

rc = readn( fd, ( char * )&reclen, sizeof( u_int32_t ) );
if ( rc != sizeof( u_int32_t ) )
return rc < 0 ? -1 : 0;
reclen = ntohl( reclen );
if ( reclen > len )
{

while ( reclen > 0 )
{
rc = readn( fd, bp, len );
if ( rc != len )
return rc < 0 ? -1 : 0;
reclen -= len;
if ( reclen < len )
len = reclen;
}
set_errno( EMSGSIZE );
return -1;
}

//超过长度就返回该字符串长度
rc = readn( fd, bp, reclen );
if ( rc != reclen )
return rc < 0 ? -1 : 0;
return rc;
}

int readn( SOCKET fd, char *bp, size_t len)
{
int cnt;
int rc;

cnt = len;
while ( cnt > 0 )
{
rc = recv( fd, bp, cnt, 0 );
if ( rc < 0 )
{
if ( errno == EINTR )
continue;
return -1;
}

if ( rc == 0 ) return len - cnt;

bp += rc;
cnt -= rc;
}
return len;
}

它这里recv了两次呀, 第一次 rc = readn( fd, ( char *
)&reclen, sizeof( u_int32_t ) );

这里最少也读了一次呀....
while ( reclen > 0 )
{
rc = readn( fd, bp, len );

这到底怎么理解recv呢??????????

Wooyea

unread,
Dec 11, 2006, 1:21:59 AM12/11/06
to dev4s...@googlegroups.com
没看代码
 
发一个包,只能收一次。第二次再收到就是下一个包了。
确定接收缓冲大于收包的最大长度就行。
socket 接口一般都可能取出接收到的数据长度(bytes)。
不大于64K可以不分包,省事。

楠楠

unread,
Dec 11, 2006, 1:37:33 AM12/11/06
to 高性能网络编程邮件列表
发件人: Wooyea

发一个包,只能收一次。第二次再收到就是下一个包了。

确定接收缓冲大于收包的最大长度就行。
socket
接口一般都可能取出接收到的数据长度(bytes)。
不大于64K可以不分包,省事。


你没看一下程序吧,
它就是send了一次,却recv了两次呀..........

Wooyea

unread,
Dec 11, 2006, 1:46:28 AM12/11/06
to dev4s...@googlegroups.com
呵,是没看代码
算我猜对了
 
一个包没法分两次接呀

楠楠

unread,
Dec 11, 2006, 2:23:02 AM12/11/06
to 高性能网络编程邮件列表
服了你了, 到底你明不明白我的问题?

一个包没法分两次接?
我贴出来的例子就是分两次接的,第一次recv读长度,
第二次recv读内容...............
我是想问, 为什么可以这样????

楠楠

unread,
Dec 11, 2006, 2:42:16 AM12/11/06
to 高性能网络编程邮件列表

接收方面,TCP协议没有数据边界,即一个发出的数据可能会在多次接收中才能全部接收完,而且多次发出的少量数据收可能在一次接收中全部收到了,为什么上面贴的源码可以接收两次,不明白.

sunway

unread,
Dec 11, 2006, 2:47:27 AM12/11/06
to 高性能网络编程邮件列表
开大缓冲一次接收完全部的数据然后慢慢拆分不就OK了,分2次接收增加了API的调用次数,降低了性能,没有任何实际意义。

zhu weijie

unread,
Dec 11, 2006, 3:31:00 AM12/11/06
to dev4s...@googlegroups.com

是可以这样的,你看一下tcp接收的缓存机制就知道了,它是可靠的缓存机制,同时tcp的输入输出都是流,流本身没有记录边界,所以你可以每次任意的从缓存中读取,不会出现丢弃数据的现象。这是流的特性。而udp不同了,它是基于记录的,一次读取是读取整个记录。
On 12/11/06, sunway <sunh...@gmail.com> wrote:
开大缓冲一次接收完全部的数据然后慢慢拆分不就OK了,分2次接收增加了API的调用次数,降低了性能,没有任何实际意义。

Wooyea

unread,
Dec 11, 2006, 3:54:59 AM12/11/06
to dev4s...@googlegroups.com
鼻塞,眼泪花花的没敢看代码,以为你是用udp 的了
 
开大缓冲,如果不是定长的粘包了也挺麻烦。
 
你的疑惑是什么?我理解能力有点问题。
 
很少用直接winsock
不知你的问题是不是与recv的工作方式有关?由recv最后那个参数
 
 

sunway

unread,
Dec 11, 2006, 4:12:39 AM12/11/06
to 高性能网络编程邮件列表
确实可以这样,但是这么编码实际比一个缓冲读全部内容编码上还要麻烦一些。我早期也喜欢这么做,后来都改成一次调用,原因就是简单,可扩展性好。
Message has been deleted

sunbi...@gmail.com

unread,
Dec 13, 2006, 8:31:16 AM12/13/06
to 高性能网络编程邮件列表
>> 它这里recv了两次呀, 第一次 rc = readn( fd, ( char * )&reclen, sizeof( u_int32_t ) );
这里只从recv buffer里读了sizeof( u_int32_t )个字节

>>这里最少也读了一次呀....
>>while ( reclen > 0 )
>> {
>> rc = readn( fd, bp, len );

从recv buffer中读取剩余len个字节

不知道lz是不是在这个地方有疑问,之前我发过一个帖子就误解lz的意思了。


"楠楠 写道:
"
> 接收方面,TCP协议没有数据边界,即一个发出的数据可能会在多次接收中才能全部接收完,而且多次发出的少量数据收可能在一次接收中全部收到了,为什么上面贴的源码可以接收两次,不明白.

代李

unread,
Dec 14, 2006, 1:28:04 AM12/14/06
to dev4s...@googlegroups.com
发一个包 send(12+1024)

收recv(12)
   recv(1024)
 应该可以吧.


楠楠

unread,
Dec 14, 2006, 11:25:50 PM12/14/06
to 高性能网络编程邮件列表

找到问题了,是我自己的程序处理上有些问题.....浪费了大家宝贵时间..sorry

代李

unread,
Dec 29, 2006, 10:34:37 PM12/29/06
to dev4s...@googlegroups.com
都不说说是什么问题,有没有该注意的地方什么的。

在06-12-15,楠楠 <wu_yan...@yahoo.com.cn> 写道:

找到问题了,是我自己的程序处理上有些问题.....浪费了大家宝贵时间..sorry

Reply all
Reply to author
Forward
0 new messages