目标:我有一个Socket server,需要从一个socket client接收数据,client的数据包结构是一个固定长度的包头,记录该包数据体个数及单个数据体长度,在后面跟着发送指定个数的数据体。client端的数据包(包头+N*包体)是用一个send ()发送的。服务端就是接收该包并处理。实现:在服务端接收时,我是先用一个receive()接收包头,然后取出单个包体长度和数据包个数。然后循环取数据包体,每次只取一个包体,处理。问题:如果数据包体个数不多(3--5),这样实现没问题,但是如果多了,比如8个(也不算很多啊!),服务端就接收不全数据。通常是到第3个数据包体,对方socket连接就断掉了。不知道是什么原因。好像不是时间的问题,因为这个响应时间<1秒。而我断点跟踪receive()时,超过10秒,第2,3个包体还是能够接收,但是后来的就接收不到了。我想到的解决办法是,可以先计算出所有包体一共需要缓冲区,然后动态分配一下内存,然后一次全部接收。这应该可以解决问题。但是我不明白为什么分多次接收数据包不行?经验告诉我, 多个send()发送一个数据,用一个receive()可能收不全。但是一个send(),用多个receive()应该可以收到啊?请高人指点。

解决方案 »

  1.   

    应该可以收到!
    你的socket是阻塞还是非阻塞?socket断开报的什么错?
      

  2.   

    多谢各位!
    to alanwang_(alan) :
    阻塞的。to jennyvenus(JennyVenus) :
    发送是一次发送,接收分多次to liguangyi() :
    你指的是socket本身的缓冲区?怎么配置啊?如果指的我程序中的缓冲区,我的程序每次只接收指定大小的数据包,不会同时收到2个。
      

  3.   

    to alanwang_(alan):
    重新跟踪一下,socket并没有断开,而是接收的数据<指定的数据包大小!这是怎么产生的呢?网络问题应该关系不大,100M局域网。
      

  4.   

    我觉得liguangyi() 说得有道理
      

  5.   

    一点资料供参考
    http://expert.csdn.net/Expert/topic/1030/1030019.xml?temp=.4715845
      

  6.   

    TCP底层拆分包或合并包很正常,这不仅与网络有关,还与发送和接受端的机器有关系。
    如果你接受的数据小于指定数据包的大小,就再接收一次,接收的大小是这个包剩余数据的大小,然后把两次接收的合并就可以了。如果还不够,重复。
    如果send是阻赛的,并且成功返回,发送端就应该没有问题,缓冲区也足够大了。
    跟接收的时间没有关系。我猜想你有可能是把接收下来的数据丢弃了
      

  7.   

    看一下代码吧,只留下总要的部分了
    CSocket sockTemp;
    ... for(int j=0;j<iRecordCount;j++)
    {
    ZeroMemory(&pkBody,sizeof(pkBody));
    ret = sockTemp.Receive(&pkBody,sizeof(pkBody),0);
    if(ret<sizeof(pkBody))
    throw CMyException(...);

    do others...;
    }
      

  8.   

    而如果改成这样:
    int iBufferLen=iRecordCount*sizeof(pkBody);
    ret = sockTemp.Receive(bBuffer,iBufferLen,0);数据就收全了!
      

  9.   

    你是异步接收?socket是非阻塞的?
    即使设置成阻塞,ret = sockTemp.Receive(bBuffer,iBufferLen,0);也有可能无法收到全部数据!
    必须要考虑0 < ret < iBufferLen的情况:继续接收iBufferLen - ret字节,然后合并。
      

  10.   

    CSocket是异步的啊?怎么设成同步的?哪个参数表明是同步还是异步的?
      

  11.   

    随便问一句,如何去以毫秒为单位的时间间隔,CTime::GetTime()返回的是以秒为单位的数字。 CTime tBegin,tEnd;
    SYSTEMTIME timeBegin,timeEnd;
    MessageBox("开始测试");
    tBegin=CTime::GetCurrentTime();
    GetSystemTime(&timeBegin);
    Sleep(2000);
    tEnd=CTime::GetCurrentTime();
    GetSystemTime(&timeEnd);

    CTimeSpan span=(tEnd-tBegin);
    CString tmp;
    tmp.Format("%d;%d;%ld,%ld,%ld",span.GetTotalSeconds(),tEnd.GetTime()-tBegin.GetTime(),tBegin.GetTime(),tEnd.GetTime(),timeEnd.wMilliseconds-timeBegin.wMilliseconds);2种方法好像都不对!
      

  12.   

    如果你没有特殊设置,CSocket默认是异步的!你每次接收的数据不一定是你在CSocket.Receive中传入的长度参数,有时会比它小。如果接收的数据小于需要的长度,就再等待FD_READ,等到之后接收数据,此时长度应设成 iBufferLen-ret,这样你最多接收iBufferLen-ret长度的数据,不会错误的把下一个包的数据接收下来。把两次(两次不行就多次)接收的数据合并,就是一个完整包了。后续的同样send端也是CSocket?
    GetTickCount()可以得到从系统启动开始,到现在经过的ms数,可用来计时
      

  13.   

    同意上一层的观点。另外一个解决办法是:例:Recv(){ Receive(...)},以后调用Receive的地方调用Recv就行了。