Чтение структуры внутри структуры с помощью функции распаковки PHP
-
20-09-2019 - |
Вопрос
Я хочу знать, как читать структуру внутри структуры через PHP. распаковать функция.Когда я получаю пакет 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)
};
Решение
$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
:"длинная подпись (всегда 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;
}
Другие советы
Я использую это:
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;
}
}
И все работает нормально!