问题描述:
1、首先我使用IdUDP进行通信,我的界面上既有client也有server。
2、UDP包发送的内容是我自己定义的格式,其中有些数据是变长的,为了处理方便我定义了结构体(packed record)来保存打算发送出去的数据,变长部分我使用了动态数组(array of byte)
3、发送的时候没有问题,因为我可以根据内容长度使用setlength给那个动态数组先确定大小。
4、但是接受的时候,我用了相同的packed record结构体作为buff来接受数据,这时候问题出现了,因为我接受之前不知道数据的长度,所以就没有办法给那个动态数组setlength,因此就没有办法接受到那部分数据。我的问题是:
1、我怎么能够预先知道数据体的长度,从而给动态数组指定长度。
2、如果1不可行,那么我能否可以读一次之后,使用独到的结果再给动态数组指定长度,然后接着再读一次buff,我想知道这样可不可行,buff中的值是不是读了一次之后就更新了???
3、有没有人有更好的方法,但是不要告诉我用静态数足,因为我的数据长度的变化范围太大了,不现实。

解决方案 »

  1.   

    記得以前有討論過得 樓主 搜索一下...
    ms得經典做法:typedef _tagStruct struct
    {
      int  nCmd;
      int  nLen;
      char szData[1];
    } DynamicStruct;看到了沒?..
      

  2.   

    MyStruct=record
        nCmd:integer;
        nLength:integer;
        szData:Array of char;
    end;
    接收时,先接收2个整型的size数据,转换成cmd和length,再根据length来动态社定szdata的长度,然后再接收length长度的szData数据。
      

  3.   

    IDUdpServer的Read事件里的数据都在流AData:Stream里,你怎样处理都可以。读几次都没问题。
      

  4.   

    to:gzmhero(hihihi) 还有问题
    我的TR的定义如下
    -------------------------------
    TCommand=packed record
                    version:byte; //协议版本号
                    totalPack:byte;//总包数
                    packNumber:byte;//包序号
                    equipType:byte; //设备类型
                    commandNumber:byte;//命令编号
                    apIp1:byte; //网管接入点ip
                    apIp2:byte;
                    apIp3:byte;
                    apIp4:byte;
                    equipNumber:byte;//设备编号
                    answer:byte;//应答标志
                    commandLength:byte;//命令体长度
                    commandData:array of byte; //命令数据
                    //commandData:string;
            end;
            TR=packed record          //数据体定义
                    head:char; //起始标志单元
                    crc:word;//crc校验位
                    Command:TCommand;//命令体
                    tail:char;//结束标志单元
            end;
    ------------------------------------------------------
    接受数据的程序如下
    ------------------------------------------------------
    procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
      ABinding: TIdSocketHandle);
    var blockComm,tempComm:TR;
        templength:integer;
    begin
            AData.ReadBuffer(tempComm,AData.Size);
            templength:=tempComm.Command.commandLength;
            setlength(blockComm.Command.commandData,blockComm.Command.commandLength);
            AData.ReadBuffer(blockComm,AData.Size);//这里提示错误.....高手帮我!!!!end;
      

  5.   

    AData.ReadBuffer(tempComm,AData.Size);
            templength:=tempComm.Command.commandLength;
            setlength(blockComm.Command.commandData,blockComm.Command.commandLength);
            AData.ReadBuffer(blockComm,AData.Size);//这里提示错误.....高手帮我!!!!///////////////////////////流的指针位置移动了,要移回头,才能正确读出。
    AData.Position:=0;//////////////////////////////////////////
    AData.ReadBuffer(blockComm,AData.Size);
      

  6.   

    我用上面的方法试了,但是blockComm.Command.commandData还是读不到内容;
    是不是我发送的有问题,现在将发送的代码也贴出来
    -------------------------------------------------------------
    procedure TTC10BE1.TransferModeSet(DeviceIP:string;set1,set2,set3,set4:byte);
    var blockComm:TR;        //TR的格式上面有详细的定义,有没有可能问题出在结构体上???
        tempString,tempIP:string;//这两个用来处理IP使他能够分成4字节发送出去
        pt:^variant;              //发送之前使用CRC校验,需要用到指针
    begin
            blockComm.head:='X';
            blockComm.Command.version:=1;
            blockComm.Command.totalPack:=1;
            blockComm.Command.packNumber:=1;
            blockComm.Command.equipType:=self.DeviceStyle;
            blockComm.Command.commandNumber:=30;
    //----------------处理IP------------------------------//
            tempIP:=self.DeviceIP;                          //DeviceIP是一个私有成员变量
            tempString:=leftStr(tempIP,pos('.',tempIP)-1);
            blockComm.Command.apIp1:=StrToInt(tempString);                    //1
            tempIP:=rightStr(tempIP,length(tempIP)-pos('.',tempIP));
            tempString:=leftStr(tempIP,Pos('.',tempIP)-1);
            blockComm.Command.apIp2:=StrToInt(tempString);                   //2
            tempIP:=rightStr(tempIP,length(tempIP)-pos('.',tempIP));
            tempString:=leftStr(tempIP,Pos('.',tempIP)-1);
            blockComm.Command.apIp3:=StrToInt(tempString);                    //3
            tempIP:=rightStr(tempIP,length(tempIP)-pos('.',tempIP));
            //tempString:=leftStr(DeviceIP,Pos('.',DeviceIP)-1);
            blockComm.Command.apIp4:=StrToInt(tempIP);                    //4
    //--------------完毕------------------------------------//
            blockComm.Command.equipNumber:=self.DeviceID;
            blockComm.Command.answer:=0;
            blockComm.Command.commandLength:=6;
            setLength(blockComm.Command.commandData,6);
            blockComm.Command.commandData[0]:=set1;
            blockComm.Command.commandData[1]:=set2;
            blockComm.Command.commandData[2]:=set3;
            blockComm.Command.commandData[3]:=set4;
            blockComm.Command.commandData[4]:=0;
            blockComm.Command.commandData[5]:=0;
    //-------------CRC校验-------------------------------------//
            pt:[email protected];
            blockComm.crc:=GetCrc16(pt,sizeof(blockComm.Command));
            blockComm.tail:='X';
    //--------------发送---------------------------------------//
            frmMain.UDPSend.SendBuffer(DeviceIP,RemotePort,blockComm,sizeof(blockComm));//调用主窗口的UDPClient发送数据包
    end;
      

  7.   

    通常的做法是将数据体的长度一起发过来
    就像 beyondtkl和gzmhero说得那样你要重新修改结构体的结构,加一个长度字段
      

  8.   

    我的commandlength是用来记录commandData的长度的
    我现在接受到的数据其他的都可以,就连结尾的Tail的'X'都能接收到
    但是唯独commandData没有内容,我现在在想是不是他的数据类型造成的
    因为commandData是动态数组如果在结构体当中添加一个length字段有什么好处?
    我对这个解决房案的精妙之处不是太了解
    是用这个值去判断发送的数据是否全部接受了吗?
    ----------------------------------------------
    额外一句话,今天刚刚看到AD的照片,呵呵,真酷!!!
      

  9.   

    MyStruct=record
        nCmd:integer;
        nLength:integer;
        szData:Array of char;
    end;
    接收时,先接收2个整型的size数据,转换成cmd和length,再根据length来动态社定szdata的长度,然后再接收length长度的szData数据。
    -------------------------------------------------------
    可以先从AData读取2个证型的size数据吗?是不是这样AData.readbuffer(MyStruct,2);
    这个控件我真的是第一次接触,感觉她出的问题都莫名其妙(还是自己了解的不深)
    对于AData,Tstream类型的数据,我在想他是不是把每次接受到的数据都按顺序的排在AData里面,那么如果有新数据放进来会发生什么事情哪?这个数据是不是也放到了AData里面拿?如果我第二次触发Onread事件的时候,我再用AData.Position:=0;//////////////////////////////////////////
    那么这个时候我读到的本次受到的新数据的开头,还是原来数据的开头那?如果是现在新数据的开头,那么原来的数据拿?被冲掉了吗...
    buffersize决定的是什么?如果buffersize设置很小,一个包的数据存不下又会发生什么情况那?
    希望大家讨论讨论...
      

  10.   

    是每次事件产生,都会重新写这个流。后面的会覆盖前面的数据。存不下,会出现异常提示。可以用另一种方法:
    MyStruct=record
        nCmd:integer;
        nLength:integer;
        szData:Array[0..0] of char;
    end;
    然后先把所有接收的数据写到一个缓冲区buf中,然后通过强制结构变换取得想要的值。
    或者就在缓冲区buf中,取指定位置的数据。参见我的回答
    http://community.csdn.net/Expert/topic/3824/3824705.xml?temp=.6447412
      

  11.   

    谢谢gzmhero(hihihi)一直对这个帖子的关注...
    你给我的帖子里面竟然还有我的回复,呵呵
    我说的是:高级话题,不懂...
    哈哈哈哈哈...
      

  12.   

    我的TR的定义如下
    -------------------------------
    TCommand=packed record
                    version:byte; //协议版本号
                    totalPack:byte;//总包数
                    packNumber:byte;//包序号
                    equipType:byte; //设备类型
                    commandNumber:byte;//命令编号
                    apIp1:byte; //网管接入点ip
                    apIp2:byte;
                    apIp3:byte;
                    apIp4:byte;
                    equipNumber:byte;//设备编号
                    answer:byte;//应答标志
                    commandLength:byte;//命令体长度
                    commandData:array of byte; //命令数据
                    //commandData:string;
            end;
            TR=packed record          //数据体定义
                    head:char; //起始标志单元
                    crc:word;//crc校验位
                    Command:TCommand;//命令体
                    tail:char;//结束标志单元
            end;
    ------------------------------------------------------