小弟我用TIdTcpClient和TIdTcpServer传递XML数据,在TIdTcpClient向TIdTcpServer传递时没有问题,但是TIdTcpServer向TIdTcpClient 传递时,如果XML中节点的属性过多时,TIdTcpClient只接收了一部分数据,就会报错;还有就是因为XML不能传递中文字符,我就用Base64Encoder进行了编码,但是编码后的字符串比较长,TIdTcpClient也只能接收了一部分数据。不知道是为什么~~!有那位大哥知道的告诉一下,因为我没有长时间上这个,没有积分相送,不好意思~~!谢谢

解决方案 »

  1.   

    补充:XMl数据是先转换成TMemoryStream再传送的~~!
      

  2.   

    http://www.delphifans.com/SoftView/SoftView_836.html
    看看这个demo吧
      

  3.   

    把相对应的filestream修改成你的memorystream就可以了
      

  4.   

    应该是文件比较大,通讯不能一包发送接收完整,所以你解析的时候是个不完整的Stream的问题。
      

  5.   

    To:失踪的月亮
       我已经转换的是TMemoryStream。而且client to server 时没有问题~~!
    To:hihihi
       文件也不大的~~~!
    下面是自己的XML编码。是登录的数据,没有参数,数据返回,只有登录结果在SwapData 的rstint和rststr中返回结果 
         tmpXml.XML.Add('<?xml version="1.0"?>');
         tmpXml.XML.Add('<SwapData  CommandID="1000" ReturnType="" RstInt="1" RstStr="Success!">');
         tmpXml.XML.Add('  <BaseParam>');
         tmpXml.XML.Add('    <WaitingFormHandle  Value=""/>');
         tmpXml.XML.Add('    <WaitFormHandle   Value=""/>');
         tmpXml.XML.Add('  </BaseParam>');
         tmpXml.XML.Add('  <SubmitParam>');
         tmpXml.XML.Add('    <UserID  Value="" ParamType="ftstring"/>');
         tmpXml.XML.Add('    <Password  Value=""/>');
         tmpXml.XML.Add('    <UserType  Value=""/>');
         tmpXml.XML.Add('  </SubmitParam>');
         tmpXml.XML.Add('  <ReturnParam>');
         tmpXml.XML.Add('  </ReturnParam>');
         tmpXml.XML.Add('  <ReturnData>');
         tmpXml.XML.Add('  </ReturnData>');
         tmpXml.XML.Add('</SwapData>');
      

  6.   

    server to client 你需要改写你的idtcpclient 给它加入一个接收数据的线程
      

  7.   


    这是我以前写过的一个为idtcpclient添加接收数据线程的一个组件,你新建一个package,把这个文件加入进去,然后编译安装,在你的组件面板里面会出现一个
    ldx组件页,使用里面的TLdxTCPClient组件里的DataAvailable事件,看看效果吧,good luck!
    unit LdxTCPClient;interfaceuses
      Classes, SysUtils, IdGlobal, IdStack, IdTCPClient, IdTCPConnection;type
      TTCPClientReader = class;  TLdxTCPClient = class(TIdTCPClient)
      private
        FTCPClientReader: TTCPClientReader;
        FOnDataAvailable: TNotifyEvent;
        procedure DoDataAvailableEvent;
      public
        procedure Connect(const ATimeout: Integer = IdTimeoutDefault); override;
        procedure Disconnect; override;
      published
        property OnDataAvailable: TNotifyEvent read FOnDataAvailable write FOnDataAvailable;
      end;  TTCPClientReader = class(TThread)
      protected
        FTCPClient: TLdxTCPClient;
        InpLastSize: Integer;
      public
        constructor Create(LdxTCPClient: TLdxTCPClient);
        procedure Execute; override;
      end;
      procedure Register;
    implementation{ TTCPClient }procedure TLdxTCPClient.Connect(const ATimeout: Integer);
    begin
      inherited Connect(ATimeout);
      if Connected then
        FTCPClientReader := TTCPClientReader.Create(Self);
    end;procedure TLdxTCPClient.Disconnect;
    begin
      if Assigned(FTCPClientReader) then FTCPClientReader.Terminate;
      inherited Disconnect;
    end;procedure TLdxTCPClient.DoDataAvailableEvent;
    begin
      if Assigned(FOnDataAvailable) then FOnDataAvailable(Self);
    end;{ TTCPClientReader }constructor TTCPClientReader.Create(LdxTCPClient: TLdxTCPClient);
    begin
      inherited Create(False);
      InpLastSize := 0;
      FTCPClient := LdxTCPClient;
      FreeOnTerminate := True;
    end;procedure TTCPClientReader.Execute;
    var
      InpSize: Integer;
    begin
      while (not Terminated) and (FTCPClient.Connected) do
      begin
        try
          InpSize := FTCPClient.InputBuffer.Size;
          if (InpSize = 0) or (InpSize = InpLastSize) then
            InpLastSize := InpSize + FTCPClient.ReadFromStack(False, -1, False);
          FTCPClient.CheckForDisconnect(False, True);
          if FTCPClient.Connected then
            Synchronize(FTCPClient.DoDataAvailableEvent);
        except
          if FTCPClient.Connected then FTCPClient.Disconnect;
          Terminate;
        end;
      end;
    end;
    procedure Register;
    BEGIN
      RegisterComponents('LDX',[TLdxTCPClient]);
    END;end.
      

  8.   

    我很奇怪的是TCPClient/TCPServer传递数据跟XML有半点关系吗?把XML数据当成普通的二进制流传,会丢包?
      

  9.   

    To:失踪的月亮,
      我自己写好了接收线程,可以接收数据的,如果XML中节点的属性不多时,或是值的字符串不长时是没有问题的,比如我的XML编码中password用Base64Encoder编码后比较长时 ,从server  to client 时,client接收时就会有问题,好像整个memorystream数据只接收到一部分,但是password值不长的时候是没有问题的,可以接收。To:unsigned 
       我也不知道是为什么啊,要是因为线程的编码问题我也知道怎么处理了。
      

  10.   

    你发送的时候,先发送一个4字节的长度(WriteInteger),然后再发送实际的数据,接收端先接收4字节的长度(ReadInteger),然后根据这个长度来接收数据,不就可以完成了。
      

  11.   


    上面5楼说过了,是你发送接收方面的问题,和XML什么的没关系。
    可能你的网络不顺畅或者文件稍大一点,一次性发送出去,接收可能产生几次,每次都是不完整的,要自己处理一下接收的包,合并成一个完整的才行。
    也有可能,你发送端发送几次数据,接收端只产生一次,一次就接收了几包,同样要处理,分解成几个包来处理。
      

  12.   

    TCP/IP传送跟XML没有任何关系,你出现这个问题的情况是,你一次过发送大量数据,实际上系统是分为很多个包发送,但是你的接收程序只收到其中个别包,因此,你要先发送“XML大小”,然后再发送XML,接收端接收累积到"XML大小",再重组为XML文件。这是做大数据量网络传输,经常遇到的问题。
      

  13.   

        我说了,文件内容很少,不会出现要分几次发送的情况~~!而且是client to server时 上面的情况都没有.就是下面所付的XML内容,Client to server时只设置WaitingFormHandle、WaitFormHandle的值为integer;userid、password为字符串,进行了base64encoder编码;server端接收数据后根据userid、password进行验证,结果只更新XML中SwapData的属性rstint、rststr的值,然后再传回client(在回传前把更新后的XML文件输出成文件查看没有问题的,IE可以解析)。现在是情况是client接收这个传回的数据时会出错,我把接收的部分流输出到文件后,发现password值接收了一部分后文件就结束了~~!
    下面的文件我想被传回不用被分成二次以上回传吧?tmpXml.XML.Add(' <?xml version="1.0"?>'); 
        tmpXml.XML.Add(' <SwapData  CommandID="1000" ReturnType="" RstInt="1" RstStr="Success!">'); 
        tmpXml.XML.Add('  <BaseParam>'); 
        tmpXml.XML.Add('    <WaitingFormHandle  Value=""/>'); 
        tmpXml.XML.Add('    <WaitFormHandle  Value=""/>'); 
        tmpXml.XML.Add('  </BaseParam>'); 
        tmpXml.XML.Add('  <SubmitParam>'); 
        tmpXml.XML.Add('    <UserID  Value="" ParamType="ftstring"/>'); 
        tmpXml.XML.Add('    <Password  Value=""/>'); 
        tmpXml.XML.Add('    <UserType  Value=""/>'); 
        tmpXml.XML.Add('  </SubmitParam>'); 
        tmpXml.XML.Add('  <ReturnParam>'); 
        tmpXml.XML.Add('  </ReturnParam>'); 
        tmpXml.XML.Add('  <ReturnData>'); 
        tmpXml.XML.Add('  </ReturnData>'); 
        tmpXml.XML.Add(' </SwapData>');
      

  14.   


    不要这么肯定,被不被二次以上回传是看不出的,和网络状况有关系,你无法确定会不会。
    Tcp/ip连接,不会丢失数据的。既然少数据肯定是接收的问题,不关XML啥事。要么就是接收到了,你处理的问题,用了String方式处理,把password的转换后的#0当成了结尾。
      

  15.   

    我试了,没用,server只发了一次包,client没有收全
      

  16.   

    楼主弄一个DEMO,包括服务端和客户端,以及那个有问题的XML文档。
      

  17.   

    你总给我们看你的xml干什么,你检查一下你发送的stream的size,以及接收后的stream的size,服务端使用stream发送你接收的时候就要用stream接收
      

  18.   

    下面的是服务端的Exceute程序
           if not AThread.Terminated  and AThread.Connection.Connected then
          begin
          DataStream  :=  TMemoryStream.Create ;
          try
            AThread.Connection.ReadStream(DataStream,AThread.Connection.ReadInteger,false);
            DataStream.Seek(0,soBeginning);
          except
            exit;
          end;
          FillList1    :=  TFillListView.Create ;
          FillList2    :=  TFillListView.Create ;
          HostName    :=  AThread.Connection.LocalName ;
          HostIP      :=  AThread.Connection.Binding.IP;
          if DataStream.Size >0 then
             begin
                  rstint   :=1;
                  OpenXML  := true;
                  CoInitialize(nil);
                  XmlData                                         :=  TXMLDocument.Create(nil);
                  XmlData.xml.LoadFromStream(DataStream);
                  try
                     XmlData.Active                               :=  true ;
                  except
                     XmlData.XML.Clear;
                     XmlData.XML.Add('<?xml version="1.0"?>');
                     XmlData.XML.Add('<SwapData  CommandID="0000" ReturnType="0" RstInt="" RstStr="">');
                     XmlData.XML.Add('</SwapData>');
                     XmlData.Active                             := true;
                     rstint                                      :=  0;
                     rststr                                     :=  Base64Encoder('接收网络数据错误,请重试所有操作!'); ;
                  end;
                  if  rstint = 1   then
                      begin
                      UserID                                      :=  XmlData.DocumentElement.Attributes['UserID'];
                      CommandID                                   :=  XmlData.DocumentElement.Attributes['CommandID'];
                      end;
                 if   rstint = 1  then
                      begin
                      i:= Clients.LockList.IndexOf( PClient(AThread.Data ) );
                      Clients.UnlockList ;
                      if  (i < 0) and  (CommandID<> '1000')  then
                          begin
                          rstint                                  := 0;
                          rststr                                  := Base64Encoder('请先登录后再进行其他操作!');
                          end;
                      end ;
                  if  (rstint=1)  then
                  begin
                  try
                  tmpStoredProcude1                               :=  TADOStoredProc.Create(nil);
                  tmpStoredProcude1.Connection                    :=  acConn;
                  tmpStoredProcude1.ProcedureName                 :=  'usp_GetProcedureName';
                  tmpStoredProcude1.Parameters.Refresh;
                  tmpStoredProcude1.Parameters[1].Value           :=  CommandID;
                  tmpStoredProcude1.open;
                  ProcedureName                                   :=  trim(tmpStoredProcude1.Fields[0].AsString  )  ;
                  ReturnType                                      :=  tmpStoredProcude1.Fields[1].AsInteger ;
                  tmpStoredProcude1.Close;
                  tmpStoredProcude1.Free;
                  XmlData.DocumentElement.Attributes['ReturnType']:=  ReturnType;
                  tmpStoredProcude2                               :=  TADOStoredProc.Create(nil);
                  tmpStoredProcude2.Connection                    :=  acConn;
                  except
                     rstint                                       :=0;
                     rststr                                       := Base64Encoder('数据连接错误!');
                  end;
                  end;
                  if  (rstint=1) then  begin
                  if   FillSubmitParam( XmlData, tmpStoredProcude2,ProcedureName, ReturnType)  then
                       begin
                       rstint                                 :=  tmpStoredProcude2.Parameters.parambyname('@rstint').Value ;
                       rststr                                 :=  tmpStoredProcude2.Parameters.parambyname('@rststr').Value;
                       end
                  else
                       begin
                       rstint                                  :=  0 ;
                       rststr                                  :=  Base64Encoder('提交的参数错误,请重试!');
                       end ;
                  end;
                  if   ((returntype = 1 ) or  (returntype = 3 ))  and (rstint=1) then
                       if  not FillReturnParam(XmlData, tmpStoredProcude2) then
                           begin
                           rstint                                 :=  0;
                           rststr                                 :=  Base64Encoder('返回的参数错误,请重试!');
                           end;              if   ((returntype = 2 ) or  (returntype = 3 ))  and (rstint=1) then
                       if  not FillReturnData(XmlData, tmpStoredProcude2) then
                           begin
                           rstint                                 :=  0;
                           rststr                                 :=  Base64Encoder('返回的数据错误,请重试!');
                           end;
              //  rststr  :=  'Success!';
                  if  (commandid ='1000') and (rstint=1)   then
                       begin
                       Addclient(AThread,Userid);
                       FillList2.Thread                                :=  AThread;
                       FillList2.VData                                 :=  vararrayof([userid,HostName+'('+HostIP+')',inttostr(port),datetimetostr(now),inttostr(AThread.ThreadID ),'连接登录']);
                       FillList2.FListView                             :=  ConnectionList;
                       FillList2.Resume ;
                       end; 
                 XmlData.DocumentElement.ChildNodes.Delete('SubmitParam');
                  XmlData.DocumentElement.Attributes['RstInt']    := rstint;
                  XmlData.DocumentElement.Attributes['RstStr']    := Base64Encoder(rstStr);
                  DataStream.SetSize(0);
                  XmlData.XML.SaveToStream(DataStream);
                  AThread.Connection.OpenWriteBuffer;
                  AThread.Connection.WriteStream(DataStream,true,true);
                  AThread.Connection.CloseWriteBuffer;
                  XmlData.Active  :=  false;
                  CoUnInitialize;
               end
          else
            begin
                rstint := 0;
                rststr := Base64Encoder('无命令!');
                CommandID :='Error!';
            end;
         FillList1.VData                                 :=  vararrayof([userid,HostName+'('+HostIP+')',datetimetostr(now),CommandID , inttostr(rstint) +','+  rststr]);
         FillList1.FListView                             :=  ListView1;
         FillList1.Resume ;
         FreeAndNil(DataStream);
         end;
      

  19.   

    下面的是celirnt接收线程代码
     while (not Terminated)  do
             begin
               rstint  :=  0;
               if  not FrmMain.tcConn.Connected  then
                   begin
                   Terminate  ;
                   exit;
                   end;
               try
                 i:=2;
                 StreamData.SetSize(0);
                 FrmMain.tcConn.ReadStream(GetStreamData,FrmMain.tcConn.ReadInteger ,false);
                 GetStreamData.Seek(0,soBeginning);
                 GetStreamData.SaveToFile('asdf.xml');
                 if GetStreamData.Size =TotalSize then
                    begin
                    CoInitialize(nil);
                    tmpXml              :=  TXMLDocument.Create(nil);
                    tmpXml.XML.LoadFromStream(GetStreamData);
                    try
                    tmpXml.Active       :=  true;
                    rstint              :=  tmpXml.DocumentElement.Attributes['RstInt'] ;
                    commandid           :=  tmpXml.DocumentElement.Attributes['CommandID'];
             //     rststr              :=  Base64Decoder(tmpXml.DocumentElement.Attributes['RstStr']);
                    rststr              :=  tmpXml.DocumentElement.Attributes['RstStr'];
                    WaitingFormHandle   :=  tmpXml.DocumentElement.ChildNodes[0].ChildNodes[0].Attributes['Value'];
                    WaitFormHandle      :=  tmpXml.DocumentElement.ChildNodes[0].ChildNodes[1].Attributes['Value'];
                    PostMessage(WaitingFormHandle,wm_Result,rstint,WaitFormHandle);
                    finally
                    tmpXml.Active       :=  false;
                    end;
                    CoUnInitialize;
                    GetStreamData.SetSize(0);
                    end;
                 except
                 end;
             end;
      

  20.   

    我说了,把你的程序打个包,包括客户端和服务端的代码,以及能够测试出你所说能产生错误数据的XML(如果是流程,就把整个操作流程说明一下)
      

  21.   

    这里也没有看到客户端的发送代码。另外,你的线程当中不应该频繁地CoInitialize/CoUnInitialize,在线程的Execute的循环前执行CoInitialize,循环结束后执行CoUnInitialize就可以。