######Socket接收的为什么与发送的信息有出入?######

zsgbox 2006-01-12 01:25:25
我自己定义的数据包,去发送接收,为什么老出错,为了标识包,我为每个包加了一个标识IDMark;但是赋值是对的,接收怎么有时是对的,有时候是错误的呢
//代码简介如下

//自定义数据包,用来分批发送数据
struct PicDatePack
{
short IDMark; //消息头判断 默认为1001
short Kind; //操作类型,系统设置消息,键盘消息,鼠标消息
short Status; //当前状态,开始,中间,还是结束
int Addtion; //附加消息
char Date[2048];
};
//ClientSocket的写事件
PicDatePack SendMsg;
SendMsg.IDMark = M_HEAD;
SendMsg.Kind = M_BegRemoteControl;
...
memcpy(SendMsg.Date,buf,sendsize);
SendMsg.Addtion = 0;
ClientSocket->Socket->SendBuf(&SendMsg,sizeof(PicDatePack));//SendMsg));

//ServerSocket的读事件
void *buf;
buf = new unsigned char[sizeof(PicDatePack)];
int Length = Socket->ReceiveLength();
Socket->ReceiveBuf(buf,Length);//sizeof(PicDatePack));//
PicDatePack *curMsg = (struct PicDatePack*)buf;
if(curMsg->IDMark != M_HEAD)
{
//*****************按理说不应该执行到里面来,可是经常出现这种情况为什么******************
MessageDlg("不该出现的错误,头标识有误!", mtWarning, TMsgDlgButtons() << mbOK, 0);
return;
}
//然后根据PicDatePack传过来的Kind类型去做相应的操

//我发过来包还经常有键盘鼠标消息(远程控制),为什么接收消息处理时,程序经常自己就把自己关闭了,特别是双击操作时
...全文
203 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
jwwu 2006-01-13
  • 打赏
  • 举报
回复
struct PicDatePack
{
short IDMark; //消息头判断 默认为1001
short Kind; //操作类型,系统设置消息,键盘消息,鼠标消息
short Status; //当前状态,开始,中间,还是结束
int Addtion; //附加消息
char Date[2048];
};

不是4的整数
不能用 sizeof()来计算发送包数
netsys2 2006-01-12
  • 打赏
  • 举报
回复
To :zsgbox(天心难测) ( 二级(初级)) 信誉:

while(Juge)
{
char *Tempbuf;
Tempbuf = new unsigned char[sizeof(PicDatePack)]; //不要放在循环中!效率低!内存碎片
Length = Socket->ReceiveLength();
Socket->ReceiveBuf(Tempbuf,Length);//sizeof(PicDatePack));//
TempLengh += Length;
strcat(buf,Tempbuf); //要用MEMCOPY
if(TempLengh <sizeof(PicDatePack))
Juge = true;
else
Juge = false;
delete []Tempbuf;
}
netsys2 2006-01-12
  • 打赏
  • 举报
回复
Socket->ReceiveLength() 仅能用来决定本次接收数据时,所需要的缓冲区大小,

--->ReceiveLength:Returns the number of bytes ready to be sent over the socket connection.

,如果 需要接收的数据大小已知的话
--->不知,你可以测试一下,你整个客户端。每次发2048byte,用多线程拚命发,你会发现,
每次到SERVER的数据(或者SERVER可以读的数据)绝对不一定是2048byte。
zsgbox 2006-01-12
  • 打赏
  • 举报
回复
我这样接收好象还是有刚才的问题
char *buf;
buf = new unsigned char[sizeof(PicDatePack)];
int Length = 0;
int TempLengh=0;
bool Juge = true;
while(Juge)
{
char *Tempbuf;
Tempbuf = new unsigned char[sizeof(PicDatePack)];
Length = Socket->ReceiveLength();
Socket->ReceiveBuf(Tempbuf,Length);//sizeof(PicDatePack));//
TempLengh += Length;
strcat(buf,Tempbuf);
if(TempLengh <sizeof(PicDatePack))
Juge = true;
else
Juge = false;
delete []Tempbuf;
}
PicDatePack *curMsg = (struct PicDatePack*)buf;
然后还是有刚才的问题,哪位帮指点一下
CCED136 2006-01-12
  • 打赏
  • 举报
回复
2:楼主:
需要注意两个地方,
1、客户端程序和服务器端程序的字节对齐应该一致。
2、确保数据接收完整(对方发1M,本端就应该接收 1 M,
而不管接收函数或方法调用多少次,一切以数据接收完整
为依据)。


2: netsys2(来电!)

正好和你讨论一下,引用:

正确的SERVER端接收步骤是:

1) 申请一大块内存,大约可以容纳4个完整包。

2)int Length = Socket->ReceiveLength();
把缓冲中全部数据读出,存入自己的缓冲

3)另外一个线程对自己的缓冲进行管理,分割处理数据包,分割数据包可以按长度,也可按特定的分界字符串

其中,
第二点:Socket->ReceiveLength() 的值不一定和 Socket->ReceiveBuf(...) 实际接收到缓冲区中的值一致。Socket->ReceiveLength() 仅能用来决定本次接收数据时,所需要的缓冲区大小,如果 需要接收的数据大小已知的话,Socket->ReceiveLength() 这个值基本没有什么用处。此外,判断数据是否已经接收完整,并且是否需要继续接收数据,只能依靠 Socket->ReceiveBuf(...) 返回的值,因为这个值才是实际接收到的数据长度。如果需要接收的数据长度未知,那么只能调用 Socket->ReceiveBuf(...) ,接收到多少数据算多少。

第三点:使用 tcp 协议发送数据包时,并不需要预先对需要发送的数据进行分包处理,这个操作 tcp 自动完成(虽然不是自动分包,但tcp 会自己决定每个 ip 祯携带的数据量),使用 tcp 协议自动分包功能,能够最有效的提高数据传输效率,自己指定反而不好。我曾经测试过,发送数据缓冲区的大小从 1 字节到 20 M ,调用一次 sendbuf 就可以了(当然我也按照我说的方法作了递归来确保数据全部正确发送,但每次调试的结果都是只调用了一次 sendbuf 就把所有的数据全部发送出去了[虽然并没有真正的发送到网络物理线路上])。

论坛讨论起来不方便,我们 qq 上聊怎么样:我的 17211708



zsgbox 2006-01-12
  • 打赏
  • 举报
回复
汗,多谢指点,马上改,测试
FengSC 2006-01-12
  • 打赏
  • 举报
回复
如果楼主确认每次接收都是正确的,有可能是字节对齐的问题
netsys2 2006-01-12
  • 打赏
  • 举报
回复
将字节对齐设置为 Byte,应该可以解决你的问题。
---》这个没有问题,因为他是按sizeof(PicDatePack)来取长度的。

楼主的问题在于,TCP是个流协议,

但传输时是按IP包发送的,虽然你发送时按sizeof(PicDatePack)发,但下面组IP包时不一定每次都按sizeof(PicDatePack)长度组包,这样到达SERVER是,一个数据包完全可以分成2次到达,

因此楼主的接收函数有问题了,

正确的SERVER端接收步骤是:

1) 申请一大块内存,大约可以容纳4个完整包。

2)int Length = Socket->ReceiveLength();
把缓冲中全部数据读出,存入自己的缓冲

3)另外一个线程对自己的缓冲进行管理,分割处理数据包,分割数据包可以按长度,也可按特定的分界字符串
CCED136 2006-01-12
  • 打赏
  • 举报
回复
补充:

这里:
int Length = Socket->ReceiveLength();
Socket->ReceiveBuf(buf,Length);//sizeof(PicDatePack));

需要接收的数据长度怎么是未知的?ReceiveBuf 的第二个参数应该
直接就写成 sizeof(PicDatePack),此外还需要判断 ReceiveBuf 方
法的返回值,如果这个值小于 sizeof(PicDatePack),则因该继续接收。
估计楼主的主观错误就在于以为“只需要调用一次 ReceiveBuf 方法就
可以把需要的数据接收完毕” 。

还是看看代码:
void *buf;
int len;

buf = new unsigned char[sizeof(PicDatePack)];
len = Socket->ReceiveBuf(buf,sizeof(PicDatePack));
if ( len < sizeof(PicDatePack) )
{// 如果没有接收完数据,则继续接收

//这里只是简单的再次调用了 ReceiveBuf 方法,正确的做法
// 是因该再次判断 ReceiveBuf 接收到的数据是否完整,如果
// 不完整的话,应该再次继续调用 ReceiveBuf 方法。
// 这里,最好的方法是写一个数据接收的递归函数,
// 如果数据接收不完整,则递归接收,一旦数据接收完毕,则退出递归
Socket->ReceiveBuf(buf+len, sizeof(PicDatePack)-len);
}

PicDatePack *curMsg = (struct PicDatePack*)buf;
if(curMsg->IDMark != M_HEAD)
{
...
}
//然后根据PicDatePack传过来的Kind类型去做相应的操


CCED136 2006-01-12
  • 打赏
  • 举报
回复
楼主需要注意了,我觉得这里出现错误的直接原因是程序的字节对齐问题,BCB 默认是以 4 字节对齐,这样的话,将导致你定义的结构所占字节大小发生变化,并因此而产生数据收发的错误。
你在菜单 project -> options... -> advanced compiler -> data alignment 中将字节对齐设置为 Byte,应该可以解决你的问题。

zsgbox 2006-01-12
  • 打赏
  • 举报
回复
怎么判断,如果不完整要再怎么处理,怎么接收?
我不懂电脑 2006-01-12
  • 打赏
  • 举报
回复
TCP是流协议的要注意包是否接收完整
zsgbox 2006-01-12
  • 打赏
  • 举报
回复
多谢诸位.
CCED136 2006-01-12
  • 打赏
  • 举报
回复
还有补充一下:

--->不知,你可以测试一下,你整个客户端。每次发2048byte,用多线程拚命发,你会发现,
每次到SERVER的数据(或者SERVER可以读的数据)绝对不一定是2048byte。


的确,在多线程环境,甚至就是在单线程环境下,
发送方每次以 2048 字节为数据包的大小,
尽可能快地连续发送 100 次。

在接收方当有数据到达时,调用 ReceiveLength 函数,此时 ReceiveLength 函数的返回
值是不确定的,并不是像想象的应该为 2048,这个值有可能更大,有可能更小 。
我想,在这点上,我们的意见是相同的。

下面这个观点,不知道你同不同意:尽管 ReceiveLength 返回了本次 Receive 可以接收的
数据的最大可能,但是我们可能没有必要全部接收完,应该只接收我们需要到数据即可。
例如:ReceiveLength 返回了 2048 这个值,但是我们的控制命令标准长度为 1024 ,则我
只需要开辟一个 1024 的缓冲区,并 ReceiveBuf 1024 字节的数据即可。剩下的 1024 字节
数据有可能是另外一条控制命名,也有可能是本条控制命令的数据,但我本次接收时,我并
不关心。
CCED136 2006-01-12
  • 打赏
  • 举报
回复
2: netsys2(来电!) :
1、在《C++Builder 核心编程技术》一书中,对ReceiveLength 方法是这样描述的:
“这个函数用于估计从 Socket 连接中读取数据要开辟多大的缓冲区(字节数)”

在我自己的撰写的绝大部分程序中,我都直接使用 Windows Socket API 来完成
网络通讯操作,几乎不怎么使用控件方式。因此,就我的认识和经验来看,
唯一能够确定数据接收是否完整的标志,不是每次 ReceiveBuf 前的 ReceiveLength 函数
之和,而是每次 ReceiveBuf 接收到数据的字节数之和。

因此,累计ReceiveLength 函数之和来作为实际接收到的数据量,是不可靠的。
ReceiveLength 仅代表了本次Socket 的 Receive 操作可以接收的数据量,但
并不代表 Socket 在执行 Receive 操作时实际接收到的字节数。
此外,ReceiveLength 函数,是在 Receive 操作之前进行,也就是实际的接收
操作还没有发生,ReceiveLength 函数怎么能够明确的知道即将进行的 Receive 操作
能够接收到多少数据。


2、如果 Socket 通讯过程中,每次Server 端需要接收的数据量都不能确定,那么
通讯双方的应用同步由谁来保证?因此,就个人接触到的常规的网络应用程序而言,
我还没有遇到过这种情况。其实这个话题有两个方面的情况,第一种,Client 和 Server
都是自己写的,这种情况最简单,Client 每次发多少数据,Server 接收多少数据,
程序员自己应该最清楚。第二种情况,,Client 或 Server 中,有一方是自己写的,
这种情况下,一般在发来的数据包中,肯定有一个字段代表了本次数据的长度,举例来说,
就像 Windows 的 SMB 以及 RPC 调用,虽然每次服务器方的响应数据长度都不一样,但是
在他每次的响应信息中,都有长度字段直接或间接表明了整个响应信息的数据长度。

个人经验:为了解决 Socket 通讯过程中,对每次传输数据大小的同步问题,对每次网络
通讯操作,发送方首先发送一个 4 字节的 UINT 数据,明确表明了后续数据的长度,然后
发送实际的数据,接收方首先接收 4 字节的 UINT 类型的长度标记,然后继续接收长度标
记指明的数据量,然后返回。这样就很容易的做到了通讯双方的应用和逻辑同步。


3、

你整个客户端。每次发2048byte,用多线程拚命发,你会发现,
每次到SERVER的数据(或者SERVER可以读的数据)绝对不一定是2048byte。

如果,客户端的多线程使用的 Socket 描述符是同一个的话,我没有办法完整的接收指定
线程发出的 2048 字节的连续数据,但是,如果每个线程都使用各自的 Socket 描述符的
话,要完整地接收各个线程的 2048 字节数据(更多或更少)应该是很容易的事请。


1,317

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧