从数据库中读取100条数据循环发送给客户端。每条数据总长度>8K
组件用TserverSocket and TclientSocket!
客户端如何稳定的、正确的收取每一条数据?我尝试过的方法
1、只是不断用ReceiveBuf,这种方法肯定不行,有粘包现象存在。
2、自定义数据传输协议(定义包头、包体。包头中包括包体长度)这种方法比1要好一些,但是仍然存在有的数据不能正常接受。疑问:
Receivebuf返回值到底是buf的长度还是-1
参见http://www.delphibbs.com/delphibbs/dispq.asp?lid=2140578请有经验的专家回答!不胜感激!也请有相同疑问的dfw顶之!

解决方案 »

  1.   

    只要用TCP, 就必须考虑粘包处理, 一次收到多个包或半个包的情况太常见了. 再说处理粘包拆分并不复杂, 很简单就可以做到, 当然前提是你的每个通讯包中必须包含分隔符或长度信息
      

  2.   

    TCP中是肯定要处理消息是否完整接受这样的问题,但是处理方法也的确很简单,只要将数据接收到一个指定的缓存,再加以分析,提取正确的消息,这样应该不需要作太多工作。
      

  3.   

    不要用消息模式socket,换一种比如indy组件;另外,1次发包不要超过4kb,如果超过则自己拆包发。
      

  4.   

    很简单的接收分拆包的例子:var 
      buffer: string;  // 全局变量...
    var
      RecvBuf: array [0..2048] of char;  // 局部变量
      n, len: Integer;
    begin
    // 一次读最多2k并放入全局变量中直到读完缓冲区或出错
      repeat
        len := recv(socket, RecvBuf[0], 2048, 0); 
        if len < 0 then  
          // 错误处理
        else begin
          RecvBuf[len] := #0;
          Buffer := Buffer + RecvBuf;
        end;
      until len <= 0;  n := 1;
      while (n <= Length(Buffer) - 4) and (PInteger(@Buffer[n])^ + 4 <= length(Buffer)) do  // 最少含一个包时
      begin
          // Buffer[n+4]就是接收到的数据包的起始位置, 数据包长度就是PInteger(@Buffer[n])^
          // 这里你可以根据上面的信息处理接收到的一个包
        Inc(n, PInteger(@Buffer[n])^+4);  // 定位到下一个包
      end;
      Buffer := copy(Buffer, n, Length(Buffer)); // 删掉已经处理完的整包, 剩下的就是未收完的半个包, 等待下次接收后继续处理
    end;
      

  5.   

    一点小bug, 应该是
    while (n <= Length(Buffer)-3) and ...忘了string的下标从1开始的
      

  6.   

    还有, 上面这个例子使用有限制, 就是接收数据中不能有#0, 如果要处理#0那么读取全局变量的代码可以这么写:
    var
      RecvBuf: string;
       ...
    begin
      setlength(RecvBuf, 2048);
      repeat
        len := Recv(socket, RecvBuf[1], length(recvbuf), 0);
        ....
        Buffer := Buffer + copy(RecvBuf, 1, len);
      until len<=0;
       ...
    end;如果仍希望用固定内存作为接收缓冲的话可以这么写:
    var
      RecvBuf: array [0..2056] of Char; 
       ...
    begin
      PInteger(@RecvBuf)^ := 2;  // 伪造一个string, 引用计数置为2, 防止delphi把它释放掉
      repeat
        PInteger(@RecvBuf[4])^ := Recv(socket, RecvBuf[8], 2048, 0);
          ...
        Buffer := Buffer + string(@RecvBuf[8]);
      until PInteger(@RecvBuf[4])^ <= 0;
        ...
    end;
      

  7.   

    top_hipster(无为而无所不为)  说的正解。
    把接受的数据放入缓存,每次读到一个包的头标志就检查他的包的结束表示,没收到就等待。收到了就处理这个完整的包,处理完了再从缓存中删除。
      

  8.   

    我用indy控件做了,但是到300条左右就挂了,这个indy控件的例子里面有
      

  9.   

    www.51merit.com

    WINSOCK LAN传文件(倚天篇)
    WINSOCK史上最经典的例子(屠龙篇) 
    完全满足你!
      

  10.   

    阿呆果然是高手,我看过你在一个关于网络游戏的帖子中的大论,很有收获。
    ---------------------------------------------------------------------------------
    PInteger(@RecvBuf)^ := 2;  // 伪造一个string, 引用计数置为2, 防止delphi把它释放掉
    ---------------------------------------------------------------------------------
    请问这句代码为什么可以伪造一个string,难道string[1]是从string的地址偏移了四个字节,我原来还以为是一个字节呢!这个引用计数还可以设置为几啊,分别有什么作用?你这个地方是为了防止delphi在什么时候把它释放啊,难道RecvBuf不能坚持到这个函数调用完毕吗?请赐教!