如何构建串联的短信 pdu?获取垃圾字符
Posted
技术标签:
【中文标题】如何构建串联的短信 pdu?获取垃圾字符【英文标题】:How to build concatenated sms pdu? Getting junk chars 【发布时间】:2012-08-31 04:24:14 【问题描述】:我正在尝试构建一些 php 代码以通过 telnet 将 SMS 发送到 SIM 服务器,但在发送串联消息时遇到了问题。
我已经阅读了一些关于使用填充位将编码消息 septets 转换为 octets 的内容,但我并不完全理解它是如何工作的。
我有一个类接收电话号码、消息(已拆分为最大 153 个字符)、短信总数和当前文本部分的订单号。
只要我在 $hexmessage 之前添加了“20”,它就可以工作。但是我在第一部分的开头(在我的味精的第一个字母之前)得到了一个垃圾字符,并且相同的垃圾字符替换了第二部分的第一个字母! (使用 '20' 所以它会显示一个空格,但它会显示一个三角形)
我不明白为什么,或者我必须改变什么才能让它正常工作。
我希望有人能帮助我理解我做错了什么。
这是我目前所得到的:
<?php
// Generate PDU string
public function generatePDUm($receiverNumber,$message, $sms_count, $msg_nr)
//Append filler digit if number is national
if( strlen($receiverNumber)==9)
$nacional=1;
$receiverNumber = $receiverNumber."F";
$network = substr($receiverNumber, 0, 2); //NETWORK code, used to decide the SIM Card to be used
//Check for international flags and set the number type accordingly
else
$nacional=0;
if(substr($receiverNumber, 0, 1)=='+')
$network = substr($receiverNumber, 4, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 1, 12); //remove international indicator
else if(substr($receiverNumber, 0, 2)== '00')
$network = substr($receiverNumber, 5, 2); //NETWORK code, used to decide the SIM Card to be used
$receiverNumber = substr($receiverNumber, 2, 12); //remove international indicator
/* Flag the network to be used */
switch ($network)
case "92":
$network="TMN";
break;
case "96":
$network="TMN";
break;
case "91":
$network="VODAFONE";
break;
case "93":
$network="OPTIMUS";
break;
// Receiver number must be 10 characters long ('national nr' + filler digit) or less than 13 ('351'+'national nr'). (Portugal)
if( strlen($receiverNumber) < 10 || strlen($receiverNumber) > 12)
// Error, not 10 or over 12 numbers long (Code 1)
$this->setErrorCode(1);
return false;
// Message must be 2 characters long at least
if( strlen($message) < 2 )
// Error, message too short (Code 2)
$this->setErrorCode(2);
return false;
// Message can't be longer than 153 characters. 3SMS.
if( strlen($message) > 153 )
// Error, message too long (Code 3)
$this->setErrorCode(3);
return false;
// Length of servicecenter number (00 = automatically fixed by phone)
$serviceCenterNumberLength = '00';
// SMS-? : 04=sms-deliver(recieve), 11=sms-submit, 01 = dont know but it works, 41 = SMS-SUBMIT + UDH bit (for extended/concatenated SMS)
// You can try to change this if your phone does not work with 01 command try to use 11 command
$smsType = '41';
// TP Message Reference: (placeholder), let the phone set the message reference number itself
$messageRef = '00';
// Number length. If national -> 9, if international -> 12
if($nacional==1)
$numberLength = '09';
else
$numberLength = '0C';
// Type of phone adress: (81=Unknown=10dec, 91=InternationalFormat, 92=National?)
if($nacional==1)
$numberType = '81';
else
$numberType = '91';
// Get the PDU version of the number
$number = $this->getNumberAsPDU( $receiverNumber );
// TP-PID (Protocol Identifier)
$protocolId = '00';
// TP-DCS (Data coding scheme)
$dataCodingScheme = '00';
// TP-Validity-Period (timestamp), AA=4days expiry, disabled for SonyEricsson support.
// $validityPeriod = 'A0';
// $validityPeriod = 'AA'; // Add this if the PDU command fails
/*user data header information (05 - User Data header info length
* 00 - Information element identifier for a concatenated short message
* 03 - Information element data length
* 00 - Reference number, auto
* 0.$sms_count - total SMS nr
* 0.$msg_nr - current SMS order */
$udhi = '050003000'.$sms_count.'0'.$msg_nr;
// echo 'UDHinfo: '.$udhi."\n";
// Data length of message (in hex format)
$dataLength = $this->strToHexLen($message);
// echo 'DATA LENGHT: '.$dataLength."\n\n";
// Convert message, string > 7bits > 8bits > hex
$hexMessage = $this->bit7tohex( $this->strto7bit( $message ) );
// Create the complete PDU string
$pdu = $serviceCenterNumberLength . $smsType . $messageRef . $numberLength .
$numberType . $number . $protocolId . $dataCodingScheme . $dataLength .
$udhi . '20' .$hexMessage;
/*
* Generate the length of var $pdu (pdu/2 minus 1) as pdu format requests
* The -1 is because we don't count the first two characters '00', needed for this command: 'cmgs=24'
*/
$cmgslen = strlen($pdu)/2-1;
// Build data array to return with required information
$data = array();
$data['pdu'] = $pdu;
$data['cmgslen'] = $cmgslen;
$data['rede'] = $network;
// Return the data array with PDU information
return $data;
// Generate PDU formatted cellphone number
private function getNumberAsPDU($number)
// Length of number divided by 2 handle two characters each time
$length = strlen( $number )/2;
// Set counter to 1 for strlen
$i = 1;
$pduNumber = '';
// Loop to handle every 2 characters of the phone number. 06 12 34 56 78
while ($i <= $length)
// Get 2 characters of the complete string depending on the number of the current loop.
// Then reverse these 2 characters and put them in var $pduNumber (06 = 60)
$pduNumber .= strrev( substr( $number,$i*2-2,2) );
// Counter + 1
$i++;
// Return the generated number
return $pduNumber;
/* Function to convert ascii character to 8 bits
* Much more efficient than holding a complete ASCII table
* Thanks to Mattijs F.
*/
private function asc2bin($input, $length=8)
$bin_out = '';
// Loop through every character in the string
for($charCount=0; $charCount < strlen($input); $charCount++)
$charAscii = ord($input$charCount); // ascii value of character
$charBinary = decbin($charAscii); // decimal to binary
$charBinary = str_pad($charBinary, $length, '0', STR_PAD_LEFT);
$bin_out .= $charBinary;
// Return complete generated string
return $bin_out;
// String to 7 bits array
private function strto7bit($message)
$message = trim($message);
$length = strlen( $message );
$i = 1;
$bitArray = array();
// Loop through every character in the string
while ($i <= $length)
// Convert this character to a 7 bits value and insert it into the array
$bitArray[] = $this->asc2bin( substr( $message ,$i-1,1) ,7);
$i++;
// Return array containing 7 bits values
return $bitArray;
// Convert 8 bits binary string to hex values (like F2)
private function bit8tohex($bin, $padding=false, $uppercase=true)
$hex = '';
// Last item for counter (for-loop)
$last = strlen($bin)-1;
// Loop for every item
for($i=0; $i<=$last; $i++)
$hex += $bin[$last-$i] * pow(2,$i);
// Convert from decimal to hexadecimal
$hex = dechex($hex);
// Add a 0 (zero) if there is only 1 value returned, like 'F'
if($padding && strlen($hex) < 2 )
$hex = '0'.$hex;
// If we want the output returned as UPPERCASE do this
if($uppercase)
$hex = strtoupper($hex);
// Return the hexadecimal value
return $hex;
// Convert 7 bits binary to hex, 7 bits > 8 bits > hex
private function bit7tohex($bits)
$i = 0;
$hexOutput = '';
$running = true;
// For every 7 bits character array item
while($running)
if(count($bits)==$i+1)
$running = false;
$value = $bits[$i];
if($value=='')
$i++;
continue;
// Convert the 7 bits value to the 8 bits value
// Merge a part of the next array element and a part of the current one
// Default: new value is current value
$new = $value;
if(key_exists(($i+1), $bits))
// There is a next array item so make it 8 bits
$neededChar = 8 - strlen($value);
// Get the char;s from the next array item
$charFromNext = substr($bits[$i+1], -$neededChar);
// Remove used bit's from next array item
$bits[$i+1] = substr($bits[$i+1], 0, strlen($bits[$i+1])-$neededChar );
// New value is characters from next value and current value
$new = $charFromNext.$value;
if($new!='')
// Always make 8 bits
$new = str_pad($new, 8, '0', STR_PAD_LEFT);
// The 8 bits to hex conversion
$hexOutput .= $this->bit8tohex($new, true);
$i++;
// Return the 7bits->8bits->hexadecimal generated value
return $hexOutput;
// String to length in Hex, String > StringLength > Hex
private function strToHexLen($message)
// Length of the string (message)
$length = strlen( $message )+7; //+7 for UDH. the UDH is a total of (number of octets x bit size of octets) 6 x 8 = 48 bits long. Therefore a single bit of padding has to be prepended to the message. The UDH is therefore (bits for UDH / bits per septet) = (48 + 1)/7 = 7 septets in length.
// Hex value of this string length
$hex = dechex($length);
// Length of the hex value
$hexLength = strlen($hex);
// If the hex strng length is lower dan 2
if($hexLength < 2)
// Add a 0 (zero) before it
$hex = '0'.$hex;
// Return the hex value in UPPERCASE characters
return strtoupper($hex);
?>
【问题讨论】:
【参考方案1】:如您所知,创建串联的 SMS 消息需要您在文本消息之前添加 UDH。 UDH 成为您有效负载的一部分,从而减少了您可以在每个段中发送的字符数。
由于它已成为您有效负载的一部分,因此需要确认您的有效负载数据要求 - 即 7 位。然而,UDH 是 8 位的,这显然使事情变得复杂。
考虑以下的UDH:
050003000302
05是UDH的长度
00 是 IEI
03 是 IEDL(另外 3 个八位字节)
00 是一个参考(这个数字在你的每个连接的消息 UDH 中必须相同)
03 是消息的最大数量
02 是当前消息号。
总共有 6 个八位字节 - 相当于 48 位。这一切都很好,但是由于 UDH 实际上是您的 SMS 消息的一部分,您需要做的是添加更多位,以便实际消息从 septet 边界开始。每 7 位有一个 septet 边界,因此在这种情况下,我们必须再添加 1 位数据以使 UDH 为 49 位,然后我们可以添加我们的标准 GSM-7 编码字符。
您可以从Here 阅读更多相关信息
【讨论】:
【参考方案2】:这些问题无处不在,似乎没有人能够以任何有意义的方式回答它们。零填充通常只会让事情变得更糟。我认为解决 GMS 标准中这个设计缺陷的最简单方法是使用 8 位编码或 16 位 UCS2,即使这意味着更少的字符。这样您就无需关心字节边界的差异,这就是创建串联 SMS 如此困难的原因。
【讨论】:
以上是关于如何构建串联的短信 pdu?获取垃圾字符的主要内容,如果未能解决你的问题,请参考以下文章