用Blocking类型吧, 没有8K的限制, 方便
如果是NonBlocking类型, 最大8K, 你可以读完8K继续读, 也没问题的

解决方案 »

  1.   

    前面都是正确的,就是在接收时如果超过25条记录时( iRow := loginclinet.QueryDetailAnswer.uDetailNum;  大于25时),后面的数据就发生错误了,为空。
    请各位帮忙,会一直在线等,如果解决问题,再开贴送分100!
      

  2.   

    不过Blocking不会触发OnRead事件, 你要自己写线程读Socket
      

  3.   

    我用的是NonBlocking类型的,也知道读完8K可以继续读,但不知道怎么读,
    此结构里面有个数据包长度的:uPackLength : longword;                // 包长度怎么继续读?
    有代码更是万分感谢,
      

  4.   

    StringGrid的处理应该另开线程处理
    应确保ClientSocket1Read事件处理的足够快
      

  5.   

    一个简单的例子
    var
      Len: Integer;
      L, L2: Integer;
      CommandLength: Integer;
      BufServer: array [0..100000] of Char;
    begin
      Len := Socket.ReceiveBuf(CommandLength, SizeOf(LongWord));
      if Len <> 4 then Exit;
      CopyMemory(@BufServer[0], @CommandLength, SizeOf(LongWord));
      repeat
        L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len);  
       //并不一定一次能把收到的数据全部读取完, 所以要在这个循环中读取, 并计算每次读取的长度, 直到读取的所有长度为你的命令长度
        if L > 0 then
        begin
          Inc(Len, L);
        end;
      until (Len = CommandLength);
    end;
      

  6.   

    不想再改为Blocking了,改动量太大,这个星期内就要交货了,怎么将先读的保存起来,再将后面的与先读的放在一起,再怎么将这些数据写到我的
    stringgrid
      

  7.   

    根据uPackLength
       first 
            new(buff,uPackLength);
       ClientSocket1Read中有记录本次接受长度
       全部接受完后再显示
      

  8.   

    能否写一下完整的代码,能按我的代码写一下更好,
    现在头也点大,再加上对socket编程不是很理解
      

  9.   

    看来我的要求有点过份,
    CopyMemory(@BufServer[0], @CommandLength, SizeOf(LongWord));
    这句是什么意思?
      

  10.   

    procedure DispPacket (const loginclinet: structClientPack);  
    begin
       
       stb := loginclinet.PackHead.cAnswer;   //应答标志,是否成功
      case stb of 
        d_Error_Ok :               //赋值
            begin
                 Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation);
              iRow := loginclinet.QueryDetailAnswer.uDetailNum;
         par_table_form.StringGrid1.RowCount := iRow + 1;
         for i:=1 to iRow do
         begin
             par_table_form.StringGrid1.Cells[0,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cLogNo;
             par_table_form.StringGrid1.Cells[1,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cPhone;
             par_table_form.StringGrid1.Cells[2,i] := inttostr(loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].uAmount);
              par_table_form.StringGrid1.Cells[3,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cTime;
              par_table_form.StringGrid1.Cells[4,i] := loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cDealNo;
               if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '0' then
                   par_table_form.StringGrid1.Cells[5,i] := '成功'
              else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '1' then
               par_table_form.StringGrid1.Cells[5,i] := '无应答'
                      else if loginclinet.QueryDetailAnswer.ChargeDetail[i - 1].cReturn = '2' then
                           par_table_form.StringGrid1.Cells[5,i] := '失败';
           end;
         end;
    end;
    var buf:array[0..16384] of char;
        recCount:integer=0; 
        ClientPacklen:integer=sizeof(structClientPack);
    procedure  ClientSocket1Read(Sender:Tobject;socket:TCustomWinSocket)
    var
      Apack : structClientPack;
      len:integer;
    begin
    //如果不是连续发的话
       
       len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack));
       inc(RecCount,len);
       if RecCount=ClientPacklen then
       begin
         recCount:=0;
         move(buf,Apack,ClientPackLen);
         DispPacket(Apack);//最好新开一个Thread 单独做
       end;
       
    end;
      

  11.   

    procedure  ClientSocket1Read(Sender:Tobject;socket:TCustomWinSocket)
    var
      Apack : structClientPack;
      len:integer;
    begin
    //如果可能是连续发的话
       len:=socket.ReceiveBuf(buf[recCount],sizeof(structClientPack));
       inc(RecCount,len);
       if RecCount>=ClientPacklen then
       begin
         move(buf,Apack,ClientPackLen);
         DispPacket(Apack);//最好新开一个Thread 单独做
         dec(recCount,ClientPacklen);
         if recCount>0 then
            move(buf[ClientPacklen],buf,recCount);//没有考虑剩下的是不是一个完整的structClientPack,异步一般不可能
       end;  
    end;
      

  12.   

    用NonBlocking, 试试如下代码, 把下面的代码添加到你的前面, 后面 的处理还是你自己的procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      loginclinet : structClientPack;
      i,iRow :integer;
      Buffer: array [0..1000000] of Byte;  //字符串的长度要不小于你一个数据包的最大长度
      Len, L, L1: LongWord;                //添加 长度变量
    begin
    ///////////////////////////////////////////////////////////////////////////////
      Socket.ReceiveBuf(@Buffer, SizeOf(LongWord));   //读取一个LongWord的 包长度
      CopyMemory(@Len, @Buffer[0], SizeOf(LongWord));  //把读取的包长度放到Len中
      L := 0;
      repeat                       //循环读取收到的数据直到读取的数据长度为包长
        L1 := Socket.ReceiveBuf(@Buffer[L], Len - L)
        if L1 > 0 then Inc(L, L1);
        if L1 < 0 then Exit;            //读取错误 退出
      until L1 = Len;
      CopyMemory(@LoginClient, @Buffer, Len);
    //////////////////////////////////////////////////////////////////////////////
       stb := loginclinet.PackHead.cAnswer;   //应答标志,是否成功
    ///   你的代码.............
      

  13.   

    正在调试,可能有点问题。
    Socket.ReceiveBuf(@Buffer, SizeOf(LongWord));   //读取一个LongWord的 包长度L1 := Socket.ReceiveBuf(@Buffer[L], Len - L)编译不通过?
      

  14.   

    Sorry, 没有调试, 改代码如下
    ////////////////////////////////////////////////////////////////////////////
      Socket.ReceiveBuf(Buffer, SizeOf(LongWord));   //读取一个LongWord的 包长度
      CopyMemory(@Len, @Buffer[0], SizeOf(LongWord));  //把读取的包长度放到Len中
      L := 0;
      repeat                       //循环读取收到的数据直到读取的数据长度为包长
        L1 := Socket.ReceiveBuf(Buffer[L], Len - L)
        if L1 > 0 then Inc(L, L1);
        if L1 < 0 then Exit;            //读取错误 退出
      until L1 = Len;
      CopyMemory(@LoginClient, @Buffer, Len);
    ////////////////////////////////////////////////////////////////////////////
      

  15.   

    insert2003(高级打字员),反正你明白要点就是了。。
    小虫说得应该不错的。
    ClientRead事件处理里面,需要用一个循环读出数据,这才可确保SOCKET缓冲区数据读干净。
      

  16.   

    如果数据量大时,是多次触发onRead事件吧,而这个循环是??Socket.ReceiveBuf(Buffer, SizeOf(LongWord));   //读取一个LongWord的 包长度这里为可要读一个LongWord的 包长度?我的调试还未来通过!
      

  17.   

    RECEIVEBUF时候,BUFFER你随便使用个定长的就行,不一定要多长:)
    通过返回值判断,直到实际读出的数据小于缓冲长度。。每次读出的数据,可以写入一个TMEMORYSTREAM中,这样便于处理。。
      

  18.   

    数据量大时,服务端是一次发送过来的,而clinetsocket是不是分多次接收的,就是多次触发onread事件,
    我按小虫说的去调试,但没有通过!
      

  19.   

    小虫写的代码比较仓促,你调试不过是正常的。
    你用心理解一下,事情没有什么复杂的。你RECEIVEBUF读一次,传入参数是你缓冲区大小,返回值是你实际读出。
    当实际读出等于你缓冲区大小时候,说明在SOCKET内部缓冲里面多半可能还有数据要读。
    那这样你就得循环再次去读它。
      

  20.   

    关键是记清楚,TCP提供的仅仅是数据流服务。
    你自己的应用数据包,再从这个数据流中读取出来。
      

  21.   

    我的思路是基于多次触发onRead事件
      

  22.   

    下面是我程序中的一个例子, 肯定可以的, 楼主参考
    type
      TMsgHeader = record
        cmdLength: Longword;
        cmdID: Longword;
        cmdStatus: LongWord;
        cmdSequence: LongWord;
      end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      Len, L, CommandLength: Integer;
      Head: TMsgHeader;
    begin
      Len := 0;
      repeat       //读取一个LongWord, 利用repeat循环是避免还
                   //没有读到完整的LongWord网络就断开了
        L := Socket.ReceiveBuf(BufServer[Len], SizeOf(LongWord) - Len);
        if L > 0 then
        begin
          Inc(Len, L);
        end;
        if L < 0 then Exit; //可能网络断开
      until (Len = SizeOf(LongWord));
      CopyMemory(@CommandLength, @BufServer[0], SizeOf(LongWord));
      repeat               //循环读取剩余的数据, 直到长度为包头中的长度, 或网络断开
        L := Socket.ReceiveBuf(BufServer[Len], CommandLength - Len);
        if L > 0 then
        begin
          Inc(Len, L);
        end;
        if L < 0 then Exit; //可能网络断开
      until (Len = CommandLength);
      CopyMemory(@Head, @BufServer, SizeOf(TMsgHeader));
    //  DecodeMsg(@BufServer[0], CommandLength);
    end;
      

  23.   

    按小虫的调试基本通过。
    但还是存在问题,
    我在取数据时:
     CopyMemory(@loginclinet, @BufServer, SizeOf(structClientPack));
        stb := loginclinet.PackHead.cAnswer;
        case stb of
             d_Error_Ok :
               begin
                 Application.messagebox('查询充值明细成功!','系统提示',MB_IconInformation);             加上这一句就会报错,说什么(stack overflow)栈溢出,接着就出现CPU调试窗口,程译就运行不了了              iRow := loginclinet.QueryDetailAnswer.uDetailNum;
                  par_table_form.StringGrid1.RowCount := iRow + 1;
                  ......我把上面说提示去掉就没问题了,请问这是怎么回事,如可解决?
      

  24.   

    这也是出错的提示信息:Project ...我工程文件所在...  faulted with message:'access violation at 0x004fe87b:write of address 0x00030ffc'.Process Stoppend.Use Step Or Run to continue
    请问,如何解决!
      

  25.   

    异步接收肯定是多次触发onRead事件,你这样做肯定....
      

  26.   

    风兄,非常感谢你的关注,按你说的思路与代码进行调试时,不知为何,程序没有反应,中间的一部分代码好像跳过去了,应该是我对你的代码理解的不够深,可否在代表加上注释?
    我新开了一贴,欢迎讨论:
    http://expert.csdn.net/Expert/topic/2873/2873751.xml?temp=.3361475
      

  27.   

    postren(小虫),你好,按你短消息的提示,我把缓冲数组大小改成一个数据包的大小,没有出现打开CPU调试窗口的问题了,但还是报错。
    我在前面加了sleep(1)就没有出现问题了,觉得非常奇怪,不甚明白,请指点。我新开了一贴,欢迎讨论:
    http://expert.csdn.net/Expert/topic/2873/2873751.xml?temp=.3361475
      

  28.   

    点评一下,加入收藏:  数据量大时,发送端和接收端都可能分成多个包,但是不用去管它,我们只要知道完成数据的长度就可以
      发送端分多次发送,每次取得实发长度累加,只到达到总长度。
      发送端在onRead事件中处理,当次接收到的包连接到接收缓冲区中,该缓冲区生存期是高于本过程的,当缓冲区的数据长度达到传送的总长度时,认为一次完整的数据传输结束,这时进行后续处理,然后清空缓冲区,等待下一次传输!  不知道是不是这样!
      

  29.   

    据我所知,如果数据量大时,server端是一次发送的,而clinet端是分多次接收的,就是用异步时多次触发onRead事件。
    现在是想明白用基于风兄的思路还是小虫的思路来解决问题好?
      

  30.   

    我的先生,我理解你解决问题的迫切性。你是不是有些技术方向出错,我的感觉如果一个问题需要很大的力气来解决,那可能是你的技术上有重大的缺陷。我以前做过阻塞方式的情况。
    我的感觉是,通讯的双方需要约定发送信息的频率,需要等待一方对数据处理后,另外一方再发送数据。你是否设置了相关这方面的参数,例如:发送前检测通讯线路是否准备好,对方是否允许发送数据。我在作阻塞方式时,利用了系统的TWinSocketStream类来完成此类任务,我想非阻塞方式也需要约定发送时的一些条件。希望你编程时,已经考虑到这些因素。
    另外一个因素:你也可以考虑通讯的另外一方出现了问题。他妈的,有时别人的水平比我们想象的还次。
      

  31.   

    楼上说的相关因素考虑过,我的问题也已经解决了,只是想更深入一层了理解socket,
    技术是累积起来的,没有失败,哪来的成功!