通过 PHP 的 Unpack 函数读取结构体中的结构体

发布于 2024-08-21 08:25:19 字数 1568 浏览 14 评论 0原文

我想知道如何通过 php 的 unpack 函数读取结构中的结构。当我收到 IS_MCI 数据包时,我检查它的类型以确保它等于 ISP_MCI,然后检查 NumC 以找出该数据包中有多少个 CompCar 结构。问题是尝试通过单个函数将这些内容解压到数组中。我总是得到一个未定义的偏移量。所以,我正在寻找对此事的新看法。

你会如何处理这个数据包?

有问题的结构是这样的:

struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[8];    // car info for each player, 1 to 8 of these (NumC)
};

struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

I want to know how to read a struct within a struct via php's unpack function. When I get an IS_MCI packet, I check it's Type to make sure it's equal to ISP_MCI, and then I check NumC to find out how many CompCar structs there are within this packet. The problem is trying to unpack these contents into an array via a single function. I always get a undefined offset. So, i'm looking for some fresh eyes on the matter.

How would you handle this packet?

The struct in question is this:

struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[8];    // car info for each player, 1 to 8 of these (NumC)
};

struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

メ斷腸人バ 2024-08-28 08:25:19
$msg = 
   chr(0x20) // Size = 32 (4+1*28)
  . chr(0x1) // Type = 1
  . chr(0x0) // ReqI=0
  . chr(0x1) // NumC=1
    . chr(0x1) . chr(0x0) // node=1
    . chr(0x2) . chr(0x0)  // lap=2
    . chr(0x3) // puid=3
    . chr(0x5) // pos=5
    . chr(0x10) // info=16
    . chr(0x0) //sp3=0
    . chr(0x0) . chr(0x0) . chr(0x1) . chr(0x0) // x=65536
    . chr(0x0) . chr(0x0) . chr(0x2) . chr(0x0) // y=65536*2
    . chr(0x0) . chr(0x0) . chr(0x3) . chr(0x0)  // z=65536*3
    . chr(0x0) . chr(0x20) // speed=8192
    . chr(0x0) . chr(0x10) // dir=4096
    . chr(0x0) . chr(0x8) // heading=2048
    . chr(0x0) . chr(0x4) // AngVel=1024
;

$IS_MCI = unpack('CSize', $msg);
if ( strlen($msg) < $IS_MCI['Size'] ) {
  die("not enough data");
}
$IS_MCI += unpack('CType/CReqI/CNumC', substr($msg, 1));
$IS_MCI['Info'] = array();

for($i=0; $i<$IS_MCI['NumC']; $i++) {
  $data = substr($msg, 4+($i*28), 28);
  $IS_MCI['Info'][] = unpack('vNode/vLap/CPLID/CPosition/CInfo/CSp3/lX/lY/lZ/vSpeed/vDirection/vHeading/sAngVel', $data);
}
print_r($IS_MCI);

现在

Array
(
    [Size] => 32
    [Type] => 1
    [ReqI] => 0
    [NumC] => 1
    [Info] => Array
        (
            [0] => Array
                (
                    [Node] => 1
                    [Lap] => 2
                    [PLID] => 3
                    [Position] => 5
                    [Info] => 16
                    [Sp3] => 0
                    [X] => 65536
                    [Y] => 131072
                    [Z] => 196608
                    [Speed] => 8192
                    [Direction] => 4096
                    [Heading] => 2048
                    [AngVel] => 1024
                )

        )

)

,该代码做出了一些您可能不希望认为是理所当然的假设(即添加更多的错误/读取数据处理)。

  • 它假设数据包 ($msg) 在代码运行之前已被完全读取。您可能只想读取当前需要的部分(则不需要 substr() )。或者至少要做好消息可能以多个块的形式到达的准备。
  • 它还将 size/num 参数视为理所当然,即它不检查这些值是否可行以及是否有足够的数据可用。这绝对是你必须改变的事情。 Size 必须介于 0...228 之间,NumC 必须介于 0...8 之间,并且两个值必须匹配在一起,依此类推。
  • 还要仔细看看我在 unpack() 中使用的格式标识符。对于 word,我使用了 v,它代表“无符号短整型(始终为 16 位,小尾数字节顺序)。但是对于 int 我使用了l:“signed long(始终为32位,机器字节顺序)”,但在我的机器上搜索文档。数据字节顺序的协议

$msg 中的测试数据取自结果 。的

__declspec(align(1)) struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

__declspec(align(1)) struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[1];    // example: one element, fixed
};

int _tmain(int argc, _TCHAR* argv[])
{
  struct IS_MCI mci = {
    32, 1, 0, 1,
    { 1, 2, 3, 5, 16, 0, 65536, 65536*2, 65536*3, 8192, 4096, 2048, 1024 }
  };

  WSADATA wsaData;
  WORD wVersionRequested = MAKEWORD( 2, 2 );
   int err = WSAStartup( wVersionRequested, &wsaData );
  if ( err != 0 ) {
      /* Tell the user that we could not find a usable */
      /* WinSock DLL.                                  */
      return 1;
  }

  SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in addr; 
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  addr.sin_port = htons( 8081 );
  if ( 0!=connect( s, (SOCKADDR*) &addr, sizeof(addr) ) ) {
    printf("%X ", WSAGetLastError());
    return 0;
  }
  send(s, (const char*)&mci, sizeof(mci), 0);
  shutdown(s, SD_BOTH);
  closesocket(s);
  return 0;
}
$msg = 
   chr(0x20) // Size = 32 (4+1*28)
  . chr(0x1) // Type = 1
  . chr(0x0) // ReqI=0
  . chr(0x1) // NumC=1
    . chr(0x1) . chr(0x0) // node=1
    . chr(0x2) . chr(0x0)  // lap=2
    . chr(0x3) // puid=3
    . chr(0x5) // pos=5
    . chr(0x10) // info=16
    . chr(0x0) //sp3=0
    . chr(0x0) . chr(0x0) . chr(0x1) . chr(0x0) // x=65536
    . chr(0x0) . chr(0x0) . chr(0x2) . chr(0x0) // y=65536*2
    . chr(0x0) . chr(0x0) . chr(0x3) . chr(0x0)  // z=65536*3
    . chr(0x0) . chr(0x20) // speed=8192
    . chr(0x0) . chr(0x10) // dir=4096
    . chr(0x0) . chr(0x8) // heading=2048
    . chr(0x0) . chr(0x4) // AngVel=1024
;

$IS_MCI = unpack('CSize', $msg);
if ( strlen($msg) < $IS_MCI['Size'] ) {
  die("not enough data");
}
$IS_MCI += unpack('CType/CReqI/CNumC', substr($msg, 1));
$IS_MCI['Info'] = array();

for($i=0; $i<$IS_MCI['NumC']; $i++) {
  $data = substr($msg, 4+($i*28), 28);
  $IS_MCI['Info'][] = unpack('vNode/vLap/CPLID/CPosition/CInfo/CSp3/lX/lY/lZ/vSpeed/vDirection/vHeading/sAngVel', $data);
}
print_r($IS_MCI);

prints

Array
(
    [Size] => 32
    [Type] => 1
    [ReqI] => 0
    [NumC] => 1
    [Info] => Array
        (
            [0] => Array
                (
                    [Node] => 1
                    [Lap] => 2
                    [PLID] => 3
                    [Position] => 5
                    [Info] => 16
                    [Sp3] => 0
                    [X] => 65536
                    [Y] => 131072
                    [Z] => 196608
                    [Speed] => 8192
                    [Direction] => 4096
                    [Heading] => 2048
                    [AngVel] => 1024
                )

        )

)

Now, that code makes some assumptions that you might not want to take for granted (i.e. add a lot more error/read-data handling).

  • It assumes the packet ($msg) has been completely read before the code runs. You might want to read only the parts you currently need (no need for substr() then). Or least be prepared that the message can arrive in several chunks.
  • It also takes the size/num parameters for granted, i.e. it doesn't check if the values are feasible and enough data is available. That's definitely something you have to change. Size must be between 0...228, NumC must be between 0...8 and both values must fit together and so on.
  • Also take a closer look at the format identifiers I've used in unpack(). For wordI've used v which stands for "unsigned short (always 16 bit, little endian byte order). But for int I've used l: "signed long (always 32 bit, machine byte order)". That's ok on my machine. But search the documentation of the protocol for the endianness of the data.

The testdata in $msg has been taken from the result of

__declspec(align(1)) struct CompCar // Car info in 28 bytes - there is an array of these in the MCI (below)
{
    word    Node;       // current path node
    word    Lap;        // current lap
    byte    PLID;       // player's unique id
    byte    Position;   // current race position : 0 = unknown, 1 = leader, etc...
    byte    Info;       // flags and other info - see below
    byte    Sp3;
    int     X;          // X map (65536 = 1 metre)
    int     Y;          // Y map (65536 = 1 metre)
    int     Z;          // Z alt (65536 = 1 metre)
    word    Speed;      // speed (32768 = 100 m/s)
    word    Direction;  // direction of car's motion : 0 = world y direction, 32768 = 180 deg
    word    Heading;    // direction of forward axis : 0 = world y direction, 32768 = 180 deg
    short   AngVel;     // signed, rate of change of heading : (16384 = 360 deg/s)
};

__declspec(align(1)) struct IS_MCI // Multi Car Info - if more than 8 in race then more than one of these is sent
{
    byte    Size;       // 4 + NumC * 28
    byte    Type;       // ISP_MCI
    byte    ReqI;       // 0 unless this is a reply to an TINY_MCI request
    byte    NumC;       // number of valid CompCar structs in this packet

    CompCar Info[1];    // example: one element, fixed
};

int _tmain(int argc, _TCHAR* argv[])
{
  struct IS_MCI mci = {
    32, 1, 0, 1,
    { 1, 2, 3, 5, 16, 0, 65536, 65536*2, 65536*3, 8192, 4096, 2048, 1024 }
  };

  WSADATA wsaData;
  WORD wVersionRequested = MAKEWORD( 2, 2 );
   int err = WSAStartup( wVersionRequested, &wsaData );
  if ( err != 0 ) {
      /* Tell the user that we could not find a usable */
      /* WinSock DLL.                                  */
      return 1;
  }

  SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  sockaddr_in addr; 
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
  addr.sin_port = htons( 8081 );
  if ( 0!=connect( s, (SOCKADDR*) &addr, sizeof(addr) ) ) {
    printf("%X ", WSAGetLastError());
    return 0;
  }
  send(s, (const char*)&mci, sizeof(mci), 0);
  shutdown(s, SD_BOTH);
  closesocket(s);
  return 0;
}
云淡月浅 2024-08-28 08:25:19

我正在使用这个:


class IS_MCI extends ISP {
        public $Size;
        public $Type = ISP_MCI;
        public $ReqI;
        public $NumC;

        public function IS_MCI($data, &$CompCar) {
                $up = unpack('CSize/CType/CReqI/CNumC', $data);
                $this->Size = $up['Size'];
                $this->ReqI = $up['ReqI'];
                $this->NumC = $up['NumC'];

                $temp = array();

                $p = 4;
                for ($i = 0; $i NumC; $i++) {
                        $up2 = unpack('SNode/SLap/CPLID/CPosition/CInfo/CSp3/IX/IY/IZ/SSpeed/SDirection/SHeading/sAngVel', substr($data, $p, 28));
                        $temp[] = new CompCar($up2['Node'],$up2['Lap'],$up2['PLID'],$up2['Position'],$up2['Info'],$up2['Sp3'],$up2['X'],$up2['Y'],$up2['Z'],$
                        $p += 28;
                }
                $CompCar = $temp;
        }
}

和 CompCar 类:


class CompCar {
        public $xNode;          // current path node
        public $Lap;            // current lap
        public $PLID;           // player's unique id
        public $Position;       // current race position : 0 = unknown, 1 = leader, etc...
        public $Info;           // flags and other info - see below
        public $Sp3;
        public $X;              // X map (65536 = 1 metre)
        public $Y;              // Y map (65536 = 1 metre)
        public $Z;              // Z alt (65536 = 1 metre)
        public $Speed;          // speed (32768 = 100 m/s)
        public $Direction;      // direction of car's motion : 0 = world y direction, 32768 = 180 deg
        public $Heading;        // direction of forward axis : 0 = world y direction, 32768 = 180 deg
        public $AngVel;         // signed, rate of change of heading : (16384 = 360 deg/s)

        public $SpeedKPH;       // speed in kph
        public $SpeedMPH;       // speed in mph
        public $DirectionC;     // Direction calculated to degrees
        public $HeadingC;       // Heading calculated to degrees
        public $AngVelC;        // Calculated

        // ADDED:
        public $SpeedMS;                // speed in mps

        public function __construct($xNode,$Lap,$PLID,$Position,$Info,$Sp3,$X,$Y,$Z,$Speed,$Direction,$Heading,$AngVel) {
                $this->xNode = $xNode;
                $this->Lap = $Lap;
                $this->PLID = $PLID;
                $this->Position = $Position;
                $this->Info = $Info;
                $this->Sp3 = $Sp3;
                $this->X = $X;
                $this->Y = $Y;
                $this->Z = $Z;
                $this->Speed = $Speed;
                $this->Direction = $Direction;
                $this->Heading = $Heading;
                $this->AngVel = $AngVel;

                $this->doCalcs();
        }

        private function doCalcs() {
                // Speed Calc
                $old = $this->Speed;
                $this->SpeedKPH = ($old * (100 / 32768)) * 3.6;
                $this->SpeedMPH = $this->SpeedKPH * 0.6215;

                $this->SpeedKPH = floor($this->SpeedKPH);
                $this->SpeedMPH = floor($this->SpeedMPH);
                $this->SpeedMS = $this->SpeedKPH/3.6;

                // Direction
                $this->DirectionC = CompCar::degrees($this->Direction);

                // Heading
                $this->HeadingC = CompCar::degrees($this->Heading);

                // Angle Calcs
                $this->AngVelC = $this->AngVel * 180 / 8192;
        }

        public static function degrees($input) {
                $input = $input / 65535 * 360;
                //$input = 360 - floor($input);
                $input = floor(360 - $input);
                return $input;
        }

}

一切都工作正常!

I'm using this:


class IS_MCI extends ISP {
        public $Size;
        public $Type = ISP_MCI;
        public $ReqI;
        public $NumC;

        public function IS_MCI($data, &$CompCar) {
                $up = unpack('CSize/CType/CReqI/CNumC', $data);
                $this->Size = $up['Size'];
                $this->ReqI = $up['ReqI'];
                $this->NumC = $up['NumC'];

                $temp = array();

                $p = 4;
                for ($i = 0; $i NumC; $i++) {
                        $up2 = unpack('SNode/SLap/CPLID/CPosition/CInfo/CSp3/IX/IY/IZ/SSpeed/SDirection/SHeading/sAngVel', substr($data, $p, 28));
                        $temp[] = new CompCar($up2['Node'],$up2['Lap'],$up2['PLID'],$up2['Position'],$up2['Info'],$up2['Sp3'],$up2['X'],$up2['Y'],$up2['Z'],$
                        $p += 28;
                }
                $CompCar = $temp;
        }
}

And CompCar class:


class CompCar {
        public $xNode;          // current path node
        public $Lap;            // current lap
        public $PLID;           // player's unique id
        public $Position;       // current race position : 0 = unknown, 1 = leader, etc...
        public $Info;           // flags and other info - see below
        public $Sp3;
        public $X;              // X map (65536 = 1 metre)
        public $Y;              // Y map (65536 = 1 metre)
        public $Z;              // Z alt (65536 = 1 metre)
        public $Speed;          // speed (32768 = 100 m/s)
        public $Direction;      // direction of car's motion : 0 = world y direction, 32768 = 180 deg
        public $Heading;        // direction of forward axis : 0 = world y direction, 32768 = 180 deg
        public $AngVel;         // signed, rate of change of heading : (16384 = 360 deg/s)

        public $SpeedKPH;       // speed in kph
        public $SpeedMPH;       // speed in mph
        public $DirectionC;     // Direction calculated to degrees
        public $HeadingC;       // Heading calculated to degrees
        public $AngVelC;        // Calculated

        // ADDED:
        public $SpeedMS;                // speed in mps

        public function __construct($xNode,$Lap,$PLID,$Position,$Info,$Sp3,$X,$Y,$Z,$Speed,$Direction,$Heading,$AngVel) {
                $this->xNode = $xNode;
                $this->Lap = $Lap;
                $this->PLID = $PLID;
                $this->Position = $Position;
                $this->Info = $Info;
                $this->Sp3 = $Sp3;
                $this->X = $X;
                $this->Y = $Y;
                $this->Z = $Z;
                $this->Speed = $Speed;
                $this->Direction = $Direction;
                $this->Heading = $Heading;
                $this->AngVel = $AngVel;

                $this->doCalcs();
        }

        private function doCalcs() {
                // Speed Calc
                $old = $this->Speed;
                $this->SpeedKPH = ($old * (100 / 32768)) * 3.6;
                $this->SpeedMPH = $this->SpeedKPH * 0.6215;

                $this->SpeedKPH = floor($this->SpeedKPH);
                $this->SpeedMPH = floor($this->SpeedMPH);
                $this->SpeedMS = $this->SpeedKPH/3.6;

                // Direction
                $this->DirectionC = CompCar::degrees($this->Direction);

                // Heading
                $this->HeadingC = CompCar::degrees($this->Heading);

                // Angle Calcs
                $this->AngVelC = $this->AngVel * 180 / 8192;
        }

        public static function degrees($input) {
                $input = $input / 65535 * 360;
                //$input = 360 - floor($input);
                $input = floor(360 - $input);
                return $input;
        }

}

And everything it's working fine!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文