Jump to content

C++/delphi socket TCP/IP to PHP - memcpy/move function in php


Go to solution Solved by kicken,

Recommended Posts

Hey guys,

 

I need to rewrite a example of sockets TCP/IP communication done in c++ or delphi(have this 2 examples) to PHP, but have a problem with function memcpy(c++)/move(delphi) and with the structures used in both languages.

in both cases i have this 2 structures: (struct from delphi)

    SPMSifHdr = packed record
          ui32Synch1:       Longword;
          ui32Synch2:       Longword;
          ui16Version:      Word;
          ui32Cmd:          Longint;
          ui32BodySize:     Longint;
          end;
        
    SPMSifRegisterMsg = packed record
          hdr1:             SPMSifHdr;
          szLicense:        Array[0..19] of char;
          szApplName:       Array[0..19] of char;
          nRet:             Longint;
          end;

Code used to send message to server:

    procedure TForm1.btnRegisterClick(Sender: TObject);
    var
      RegMsg: SPMSifRegisterMsg;
      byteMsg: Array[0..(sizeof(SPMSifRegisterMsg) - 1)] of byte;
    begin
      FillChar(RegMsg, SizeOf(RegMsg), 0);
      SetHeader(CMD_REGISTER, RegMsg.hdr1);               //Set header information
    
      StrPCopy(RegMsg.szLicense, LicEdit.Text);           //build data to structure
      StrPCopy(RegMsg.szApplName, ApplEdit.Text);
    
      Move(RegMsg, byteMsg, sizeof(SPMSifRegisterMsg));   //copy structure to bytearray
    
      ClientSocket1.Socket.SendBuf(byteMsg, sizeof(SPMSifRegisterMsg));     //send the data
    end;

Problems:

1º - First in PHP how i can represent one structure like are used in c++ or delphi?

Note: Remember i need pass this kind of structure in socket_send method, in 'buf' param)

2º - In PHP what function do the same job like memcpy(c++)/move(delphi)?

Note: For me it's not important put the information necessary to send directly to the memory block, but it's important convert the information to hex(?) like happens in c++ or delphi.

Structure with information before memcpy:

QZcSO.jpg

Result of memcpy function/information send to server:

 

QtWLc.jpg
I'm desperate with this situation, for second problem i've tried used the function "pack()" but i have no ideia what format to use, because i don't now what is the format they need to receive.

 

 

In php dont exist structures so do you think this can be a right solution so send the information???

 

$packed = pack('I', $pMsgBlock->hdr1)
.pack('I', $pMsgBlock->szLicense)
.pack('I', $pMsgBlock->szAppname)
.pack('I', $pMsgBlock->nRet);

If necessary i can give more information.
 

#1 is answered with pack() (which can accept more than one argument - you don't have to call it multiple times). #2 isn't really an issue because memcpy doesn't apply to PHP but it's probably answered, again, with pack().

 

How does $packed compare with that "UUUU..." binary string? bin2hex can help you read it.

1º - First in PHP how i can represent one structure like are used in c++ or delphi?

What I would do is create a class which will be able to either deconstruct a binary string using unpack, or construct a binary string using pack. Something like:

class SPMSifHdr {
	private $synch1;
	private $synch2;
	private $version;
	private $cmd;
	private $bodySize;

	public __construct($params){		
		foreach ($params as $nm=>$v){
			$this->$nm = $v;
		}
	}

	public static function createFromBinaryString($str){
		$params = unpack("Nsynch1/Nsynch2/nversion/Ncmd/NbodySize", $str);
		return new self($params);
	}

	public function toBinaryString(){
		return pack("NNnNN", $this->synch1, $this->synch2, $this->version, $this->cmd, $this->bodySize);
	}

	public function __toString(){
		return $this->toBinaryString();
	}
}

You'll need to make sure you use the correct codes for your bit length and byte order when you pack and unpack. The structure names imply an unsigned ints, one is 16-bits the others are 32-bits. You'll need to determine which byte order to use either from documentation or by checking the existing code.

Thanks for the answers guys,

 

I have check the manual and for the first method i need execute i have this:

Message structures 1.1.1.1Message Header

All messages include a message header as the start of the message. This header contains information about what to find in the message body and the size of the message body. The format is as follows (using C++ syntax):

typedef struct {
uint32 ui32Synch1;     /*  Message synch1 = 0x55555555    */
uint32 ui32Synch2;     /*  Message synch2 = 0xaaaaaaaa    */
uint16 ui16Version;    /*  Header format version = 1      */
uint32 ui32Cmd;        /*  Command                        */
uint32 ui32BodySize;   /*  Size of message body           */
} SPMSifHdr;

18 bytes

 

1.1.1.1Message Bodies

The following section gives the format of the message bodies, based on Command ID. The message bodies are shown as type definitions using C++ syntax.

 

Unless otherwise stated, the message fields map directly onto the parameters used in the API function calls made to the tcppmsif.dll by VTCLink. Therefore, to determine what the PMS should put in each field and also what VingCard returns in each field, refer to the VingCard API function definitions.

 

PMSifRegister          62 bytes

struct SPMSifRegisterMsg
{
            SPMSifHdr                hdr1;
            char                     szLisence[20];
            char                     szApplName[20];
            int                      nRet;
};

This message structure maps onto the API function PMSifRegister.

hdr1 is the Message Header.

nRet equates to the return value of the API function.

The PMS must add NULL char at end of all char variables. Therefore, the char [20] declarations above (or equivalent Delphi declaration of [0..19] of char ) actually mean 19 usable characters plus 1 space for a NULL.

 

 

After reading your answers i have this questions:

 

1º what format use in pack function for fields of body message?

 

This is the correct way to send the message?

$packed = pack('N', "1431655765")                   //HEADER - uint32 ui32Synch1
. pack('N', "2863311530")                           //HEADER - uint32 ui32Synch2
. pack('n', 1)                                      //HEADER - uint16 ui16Version
. pack('N', 1)                                      //HEADER - uint32 ui32Cmd;
. pack('N', "44")                                   //HEADER - uint32 ui32BodySize
. pack('C', "44436412") . pack('N', " ")            //BODY - char szLisence[20]
. pack('C', "Test_Program") . pack('N', " ")        //BODY - char szApplName[20]
. pack('I', 0);                                     //BODY - int  nRet

2º How format represent space for a NULL like the ask in next sentence of documentation with pack function?

"The PMS must add NULL char at end of all char variables. Therefore, the char [20] declarations above (or equivalent Delphi declaration of [0..19] of char ) actually mean 19 usable characters plus 1 space for a NULL."

 

This is the correct way?

$packed = pack('C', "44436412") . pack('N', " "); //example

3º - To send the message i need with the function "socket_send" where the 3º param is the the number of bytes that will be sent to the remote host from buf, in this case will be the 62 bytes the manual said? (PMSifRegister   62 bytes)

Or i need to use the strlen($packed) of the message i will send?

Edited by rmartyn

1º what format use in pack function for fields of body message?

The uint32 fields would be one of these codes: l, L, N, or V

The uint16 fields would be one of: s, S, n, or v

The char field would be: c

The int field could be any of the uint32, uint16, or i, I codes.

 

As I said before, you need to determine what the byte order being used is. For stuff sent over a socket, Network Byte Order (big-endian) is common. If you can't find it anywhere in the documentation, debug the code to try and find out. You can google for ways to determine the byte order. One quick way to check would be to check the C code for calls to htons/htonl functions

 

2º How format represent space for a NULL like the ask in next sentence of documentation with pack function?

The format code x can be used to represent a nul character, or c + chr(0). PHP provides a special code for a nul-terminated string though (a), which is what would be used for the char arrays.

pack('a20', $str);
When generating $str you would ensure that it consists of only 19 characters (or less). PHP will nul chars to pad the string to 20 characters.

 

3º - To send the message i need with the function "socket_send" where the 3º param is the the number of bytes that will be sent to the remote host from buf, in this case will be the 62 bytes the manual said? (PMSifRegister   62 bytes)

Or i need to use the strlen($packed) of the message i will send?

Use strlen($packed). If you do all the packing correctly, it will equal 62.

1º Question:

Kicken, in documentation don't have no telling the byte order, but i search in google and make this 2 test's c++ code:

http://stackoverflow.com/questions/1001307/detecting-endianness-programmatically-in-a-c-program

 int num = 1;
    if(*(char *)&num == 1)
    {
        printf("\nLittle-Endian\n");
    }
    else
    {
        printf("Big-Endian\n");
    }

    if ( htonl(47) == 47 ) {
      printf("Big-Endian\n");
    } else {
      printf("\nLittle-Endian\n");
    }

And both tell me are "Little-Endian".

 

What i need to do now?

 

 

Answer to 2 and 3 question:

$packed = pack('N', "1431655765") 			//HEADER - uint32 ui32Synch1
. pack('N', "2863311530")  				//HEADER - uint32 ui32Synch2
. pack('n', 1) 						//HEADER - uint16 ui16Version
. pack('N', 1) 						//HEADER - uint32 ui32Cmd;
. pack('N', "44")  					//HEADER - uint32 ui32BodySize
. pack('C', "44436412") . pack('N', " ") 		//BODY - char szLisence[20]
. pack('C', "Test_Program") . pack('N', " ") 		//BODY - char szApplName[20]
. pack('I', 0); 					//BODY - int  nRet

echo "<br/> 5 - packed = " . $packed . "<br/> strlen " . strlen($packed);

$message = pack('N', "1431655765") 			//HEADER - uint32 ui32Synch1
. pack('N', "2863311530")  				//HEADER - uint32 ui32Synch2
. pack('n', 1) 						//HEADER - uint16 ui16Version
. pack('N', 1) 						//HEADER - uint32 ui32Cmd;
. pack('N', "44")  					//HEADER - uint32 ui32BodySize
. pack('a20', pack('c', "44436412")) 			//BODY - char szLisence[20]
. pack('a20', pack('c', "Test_Program"))  		//BODY - char szApplName[20]
. pack('I', 0); 					//BODY - int  nRet

echo "<br/> Correct Message - packed = " . $message . "<br/> strlen " . strlen($message);

I have follow your indications and i think this is the correct way to add "space for a NULL" and calc strlen, because i test my old code and new code with your indications and now strlen already give me the correct size, 62 bytes:

 

Results of echo's:

5 - packed = UUUUÿÿÿ,¼
strlen 32
Correct Mesage - packed = UUUUÿÿÿ,¼
strlen 62

I don't have permission to edit the last post:

 

But now to convert my message in PHP i use this code:

$message = pack('N', 1431655765)                  //HEADER - uint32 ui32Synch1
. pack('N', 2863311530)                           //HEADER - uint32 ui32Synch2
. pack('n', 1)                                    //HEADER - uint16 ui16Version
. pack('N', 1)                                    //HEADER - uint32 ui32Cmd;
. pack('N', 44)                                   //HEADER - uint32 ui32BodySize
. pack('a20', pack('c', "44436412"))              //BODY - char szLisence[20]
. pack('a20', pack('c', "Test_Program"))          //BODY - char szApplName[20]
. pack('I', 0);                                   //BODY - int  nRet

echo "<br/> Correct Message - packed = " . $message . "<br/> strlen " . strlen($message);

This image show the result in PHP(left) and right in c++:

s58bvn.jpg

Like we can see i'm almost there, the only diference is the caracters after "ªªªª" what possible can be wrong?

What are the values in the struct and what is the complete value of $message?

I'm afraid i don't have understand your question, but the values are this i have take an image from c++ debug before convert the structure to binary:

QZcSO.jpg

 

And the value of message sent to server in c++ after conversion is: "UUUUªªªª  "

And in php after conversion i'm getting "UUUUªªªª,1/4" like you can see in last post i've done.

 

Basicly the last 2 "charactes" are diferente.

Edited by rmartyn

. pack('a20', pack('c', "44436412")) //BODY - char szLisence[20]

. pack('a20', pack('c', "Test_Program")) //BODY - char szApplName[20]

That is not correct. You should not have the nested pack function. Just:

. pack('a20', "44436412")              //BODY - char szLisence[20]
. pack('a20', "Test_Program")          //BODY - char szApplName[20]
Notice the difference in the output:

$a = pack('a20', pack('c', '44436412')); echo bin2hex($a); -> bc00000000000000000000000000000000000000

$b = pack('a20', '44436412'); echo bin2hex($b); -> 3434343336343132000000000000000000000000

That is not correct. You should not have the nested pack function. Just:

. pack('a20', "44436412")              //BODY - char szLisence[20]
. pack('a20', "Test_Program")          //BODY - char szApplName[20]
Notice the difference in the output:

$a = pack('a20', pack('c', '44436412')); echo bin2hex($a); -> bc00000000000000000000000000000000000000

$b = pack('a20', '44436412'); echo bin2hex($b); -> 3434343336343132000000000000000000000000

 

 

I've already tried this, but this way the output it's diferent from c++:

$message = pack('N', 1431655765)                  //HEADER - uint32 ui32Synch1
. pack('N', 2863311530)                           //HEADER - uint32 ui32Synch2
. pack('n', 1)                                    //HEADER - uint16 ui16Version
. pack('N', 1)                                    //HEADER - uint32 ui32Cmd;
. pack('N', 44)                                   //HEADER - uint32 ui32BodySize
. pack('a20',  "44436412")             //BODY - char szLisence[20]
. pack('a20', "Test_Program")          //BODY - char szApplName[20]
. pack('I', 0);

echo "<br/> Message - packed = " . $message . "<br/> strlen " . strlen($message);

Output:

Message - packed = UUUUªªªª,44436412Test_Program

strlen 62

 

:-\ still not equal to c++ message after conversion, any ideia what i can do more? other solutions?

Or this is the right way?

Edited by rmartyn

I would be inclined to believe that you are not properly interpreting the debug data from your c++ program. What I have written is correct given the limited information in this thread. Your example output from the c++ code does not match what would be expected based on your responses. Your images of the debug window clearly show the string values "12345678" and "Test_Program", yet your "binary output" you claim to see does not include them anywhere, which it should I would guess. Unless those fields are being ignored in the binary output, in which case your PHP code should also ignore them.

 

My best guess would be that your sample "binary output" from your c++ app is only for the SPMSifHdr structure, and not the entire SPMSifRegisterMsg structure. Your PHP code's output however is for the entire SPMSifRegisterMsg structure, which is why the two do not match, except for the first few bytes.

 

Make sure you're comparing apples to apples, not apples to oranges.

I will clarify  the information better:

 

c++ code:

 

button code when click to regist app:

void CPMSifDlg::OnbtnReg() 
{
	SPMSifRegisterMsg *m_RegMsg;
	BYTE *pbytesSend;
	BYTE *pbytesReceive;
		
	m_RegMsg = new SPMSifRegisterMsg;

	UpdateData(TRUE);
	strcpy(m_RegMsg->szLicense, m_strLic);		//Build the data
	strcpy(m_RegMsg->szApplName, m_strAppl);
	m_RegMsg->nRet=0;
	SetHeader(&m_RegMsg->hdr1, CMD_REGISTER);
		
	pbytesSend = new BYTE[sizeof(SPMSifRegisterMsg)];
	memcpy(pbytesSend, m_RegMsg, sizeof(SPMSifRegisterMsg));     //copy m_RegMsg structure to pbytesStore

	pbytesReceive=TCPSend(pbytesSend, sizeof(SPMSifRegisterMsg));		  //sends pbytesStore and receives the response

	m_RegMsg=(SPMSifRegisterMsg*)pbytesReceive;						//copy response back to structure
	m_iRegRes=m_RegMsg->nRet;
	UpdateData(FALSE);
}

In this code, they call function TCPSend to send info to server:

BYTE *CPMSifDlg::TCPSend(BYTE *pbytesStore, int size)
{
	BYTE *pbytes;
	BOOL bEmpty;
	int nRetVal, nRead;
	int nMsgSize;
	SPMSifHdr spHdr;

	int num = 1;
    if(*(char *)&num == 1)
    {
        printf("\nLittle-Endian\n");
    }
    else
    {
        printf("Big-Endian\n");
    }

    if ( htonl(47) == 47 ) {
      printf("Big-Endian\n");
    } else {
      printf("\nLittle-Endian\n");
    }

	m_mySocket.Send((void*)pbytesStore, size);
	
	nRetVal = m_mySocket.Receive((void*)&spHdr, sizeof(SPMSifHdr), MSG_PEEK);
	
	if ((nRetVal >= sizeof(SPMSifHdr)) && (spHdr.ui32Synch1 == 0x55555555) && (spHdr.ui32Synch2 == 0xaaaaaaaa))
	{
		nMsgSize = spHdr.ui32BodySize + sizeof(SPMSifHdr);
		pbytes = new BYTE[nMsgSize];
		
		bEmpty = FALSE;
		nRead = 0;
		nRetVal = 0;
		while(!bEmpty)
		{
			nRetVal = m_mySocket.Receive(pbytes, nMsgSize);

			if (nRetVal > 0)
				nRead += nRetVal;

			if ((nRetVal <= 0) || (nRead == nMsgSize))
				bEmpty = TRUE;
			else if (nRead <= nMsgSize)
				pbytes += nRetVal;
		}
		
		if (nRead != nMsgSize)
			MessageBox("FAILED to Receive TCP message");
		else
			return pbytes;
	}
	return pbytesStore;
}

Notes about c++ program:

You ask me to make one test to check if application is little endian or big endian, and i have make this test in c++ app that communicate with the server, not in SERVER app that receive messages, because i don't have access to this code. In both tests give-me Little-Endian, but i consider this is correct because the c++ i post where they never make conversion to other kink of Endian, so i considerate they are communicating in same Endian Type, this line of thought is correct?

 

Debug:

In my first images i don't put correctly the images with correct params, but in my answers i'm considerate the right param's, so i make a new debug of various step's:

 

1 - Authentication params:

seqazq.png

 

2-Structures:

10dt5vk.png

3-params:

280sh0g.png

4 - After convert:

24lkjdj.png

5-TCP send message:

110vyu0.png

 

 

Now PHP Code:

<?php 
//correct process -> "Recommended : Register, Encode, Encode,…………Encode, Unregister"
$message = pack('N', 1431655765)                  //HEADER - uint32 ui32Synch1
. pack('N', 2863311530)                           //HEADER - uint32 ui32Synch2
. pack('n', 1)                                    //HEADER - uint16 ui16Version
. pack('N', 1)                                    //HEADER - uint32 ui32Cmd;
. pack('N', 44)                                   //HEADER - uint32 ui32BodySize
. pack('a20', "44436412")              //BODY - char szLisence[20]
. pack('a20', "Test_Program")          //BODY - char szApplName[20]
. pack('I', 0);                                   //BODY - int  nRet

echo "<br/> Correct Message - packed = " . $message . "<br/> strlen " . strlen($message);

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
     
    die("Couldn't create socket: [$errorcode] $errormsg \n");
}
 
echo "<br/>Socket created \n";
 
if (!socket_connect($sock , '###.###.###.###' , 3015)) {
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
     
    die("Could not connect: [$errorcode] $errormsg \n");
}
 
echo "<br/>Connection established \n";

//Send the message to the server
if (!socket_send($sock, $message, strlen($message), 0)) {
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("Could not send data: [$errorcode] " . $errormsg);
}
 
echo "<br/>Message send successfully";

//Now receive reply from server
if (socket_recv($sock, $buf, 2045, MSG_WAITALL) === FALSE) {
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    die("Could not receive data: [$errorcode] $errormsg \n");
}
 
socket_close($sock);

Notes:

PHP code can establish connection but and try send message the browser freeze.

 

With this image i can show you i think i'm doing all correct, the message sent(SPMSifRegisterMsg) is composed by SPMSifHdr in first position and then with the other values show in debug images.

 

Output of php pack:

echo "<br/> Correct Message - packed = " . $message . "<br/> strlen " . strlen($message);

Correct Message - packed = UUUUªªªª,44436412Test_Program
strlen 62

Edited by rmartyn
  • Solution

When you debug the c++ app and look at pbytesStore, try and get a hexdump of the value rather than the string representation. String representations can sometimes be inaccurate, a hexdump is much better when trying to compare data. You can tell that your string representation is inaccurate because the size of the data is 62-bytes, but your string view seems to only be showing what appears to be 10 bytes.

 

Once you get a hexdump of pbytesStore, compare that to a hexdump of your PHP generated string, which you can get by running $message through bin2hex

Kicken,

 

In PHP i execute this code to get hexdump:

$message = pack('N', 1431655765)                  //HEADER - uint32 ui32Synch1
. pack('N', 2863311530)                           //HEADER - uint32 ui32Synch2
. pack('n', 1)                                    //HEADER - uint16 ui16Version
. pack('N', 1)                                    //HEADER - uint32 ui32Cmd;
. pack('N', 44)                                   //HEADER - uint32 ui32BodySize
. pack('a20', "44436412")              //BODY - char szLisence[20]
. pack('a20', "Test_Program")          //BODY - char szApplName[20]
. pack('I', 0);                                   //BODY - int  nRet

echo "<br/> Correct Message - packed = " . $message . "<br/> strlen " . strlen($message);

echo "<br/> Hex dump PHP = " . bin2hex($message);

 

Output:

Message - packed = UUUUªªªª,44436412Test_Program
strlen 62
Hex dump PHP = 55555555aaaaaaaa0001000000010000002c3434343336343132000000000000000000000000546573745f50726f6772616d000000000000000000000000

 

For c++ i don't have sure about correct function to use to get the same result, so i use WireShark e take this information from there, i use PHP to make sure i am getting the correct information:

 

PHP hexDump:

55555555aaaaaaaa0001000000010000002c3434343336343132000000000000000000000000546573745f50726f6772616d000000000000000000000000

C++ hexDump:

55555555aaaaaaaa0100010000002c000000343434333634313200cdcdcdcdcdcdcdcdcdcdcd546573745f50726f6772616d00cdcdcdcdcdcdcd00000000

 

PHP wireShark Image:

usqjd.jpg

C++ wireShark Image:

2ep33id.png

 

 

Exist some differences, any ideia what can be?

Edited by rmartyn

Actualy i understand the problem is in format given in pack function, i'm trying made some changes until get the messages equal, but i'm litle stuck in  "pack('a20', "44436412")"


$message = pack('N', 1431655765)                  //HEADER - uint32 ui32Synch1
. pack('N', 2863311530)                           //HEADER - uint32 ui32Synch2
. pack('v', 1)                                    //HEADER - uint16 ui16Version
. pack('V', 1)                                    //HEADER - uint32 ui32Cmd;
. pack('V', 44)                                   //HEADER - uint32 ui32BodySize
. pack('a20', "44436412");                        //BODY - char szLisence[20]                                   

echo "<br/> Message - packed = " . $message . "<br/> strlen " . strlen($message);

echo "<br/> Hex dump PHP = " . bin2hex($message);

Output PHP:

Message - packed = UUUUªªªª,44436412
strlen 38
Hex dump PHP = 55555555aaaaaaaa0100010000002c000000343434333634313200000000000000000000000

 

C++ hexDump:

55555555aaaaaaaa0100010000002c000000343434333634313200cdcdcdcdcdcdcdcdcdcdcd546573745f50726f6772616d00cdcdcdcdcdcdcd00000000

 

In documentation tell this:

The PMS must add NULL char at end of all char variables. Therefore, the char [20] declarations above (or equivalent Delphi declaration of [0..19] of char ) actually mean 19 usable characters plus 1 space for a NULL.

 

 

I made some corrections and now the problem is this "NULL char at end of all char variables".

Edited by rmartyn

After test a recently version of same c++ app i check in wireshark the message sent to server don't have the "cdcdcdcd..." when null char, already have "00000000" like PHP as, so the only thing i need to do is pack like this way:

pack('a20', "44436412");

Thanks for helping me ;)

Edited by rmartyn

I don't think that cdcdcd... vs 000000... will matter. The way C/C++ works with strings, once it sees the first NUL character (00) it ignores everything after it. So long as the bit upto and including the first NUL character are the same, you shouldn't have an issue.

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.