转]php实现短信猫短信发送php_serial.class.php串口类

分享
      MSN和飞信报警用了两三个月了,总结如下:想收到报警消息,139的邮箱是肯定能收到的,有邮件必发短信,但是中间肯定会消耗一些时间;飞信次之十次会有九次收到吧,当然了还是比较稳定的,也比139的短信早到;MSN的话,感觉并不是每次都能收到,难道是不在线的时候发了信息上线以后就没有留言?但是有个缺点就是,这三个报警都是依赖别人的服务器,如果人家碰巧出问题了,咱也跟着吃亏?思来想去,觉得最靠谱的还是有自己的短信发送设备。
       正好手上有个GSM的modem。因为最近正迷php,所以不打算用其它的二次开发包之类的东西了。说干就干,晚上一回到家就开始找资料。这一查不要紧,你还别说,还真有用php干这事儿的。但是网上传来传去的都是那一篇通过串口+GSM MODEM发送短信示例。这个示例是windows平台下的,还用到了一个dll文件。我现在家里用的系统也换成ubuntu了,再说以后报警的服务器也是linux,总不能为了发个短信再专门换成windows系统吧。于是决定自己动手,用php的函数写linux下的程序,由此埋下了祸根。
       功夫不负有心人啊,很快在国外的一个网站找到了一个php操作串口的类:php_serial。class。于是开始动手用minicom调试modem。因为在以前单位的时候,帮别人用超级终端调试过modem,所以命令还记得一些,很快,用at指令通过minicom发送短信成功(还好俺有两个手机,要不做个实验都没法做)。ok,改用php操作modem,用at指令发英文短信,成功!good。没想到这么顺利。取得了点小胜利的我不满足只发送英文短信,于是开始尝试发送中文短信。但是直接通过at指令输入中文,modem不买帐。收到的是空白短信。这可如何是好?于是开始细细研究网上那篇通过串口+GSM MODEM发送短信示例,于是知道了,想发送中文短信就得用pdu格式,于是又开始研究pdu格式怎么回事儿。一直到睡觉的点,也没从我的modem里发出中文来。不行,先睡觉。
      一觉醒来,又细细看了一遍pdu的格式,并一遍一遍的细细学习通过串口+GSM MODEM发送短信示例的生成方法。经过一遍一遍的尝试终于可以收到一些乱码的中文了,又经过尝试终于收到了和发送字数相同的小方块的短信了。没办法到上班的点了,于是赶紧到单位。间隙的时候,问以前单位负责开始短信群发软件的同事小杨怎么用pdu格式发送短信。小杨告诉了我,要用到的at指令,以及pdu格式的生成方法,虽然没有帮我解决问题但是至少肯定了:1,我的操作方法是对的,因为可以成功收到短信;2,收到字数相等的方块,应该是编码的时候没处理好。晚上回到家又一遍一遍的看之前那篇文章,很不理解,为啥用同样的函数生成的字符串,最后的差距咋就那么大呢?边尝试边找资料,不经意间在网上我发现一篇文章说发送短信"晚上好123"的编码是:"665A4E0A597D003100320033"。我把这个串直接放到我程序里,一发送,嘿,你猜怎么着?还真收到了"晚上好123"。有点意思!于是,我也用我的函数生成"晚上好123",结果我发现了一个规律!
对的:665A4E0A597D003100320033
我的:5A660A4E7D59310032003300
      每四个字符里两两错位!!!发现这个规律以后,就简单了,因为这样就确定确实是编码那块的原因了。但是编码那块:iconv,ord,substr,sprintf好几个系统函数,一个一个熟悉,改来改去都不行,这时候已经晚上十一点半了(我习惯是十点半睡,七八点起,标准的小学生作息时间)。但是离终点这么近了,真不甘心放弃!初中时候养成的好习惯,今日事,今日毕,因为明天还有明天的事!洗把脸,重新整理一下思路:虽然大体看明白了pdu格式,但是字符串转换这块,我还真理解不透。怎么办呢?看来从根上解决这个问题,我功力是不够了。往前不行,咱可以迂回嘛。那我就自己写一个函数把这个字符串的顺序变回来!你还真别说,这招好使!而且再发其它的中文短信,也可以正常收到啦!!!呵呵,看来今晚注定好梦了。
虽然凑合能用了,但是还不知道是什么原因啊。自己搞不定不要紧,公司里我周围坐着的可全是php高手。午休的时候,把会军叫过来,说明事情的来来回回,会军看了一遍代码以后,断定是字符的高低字节问题!因为linux系统和windows系统,对unicode处理的时候,正好是高低位相反的!说实话,当时真惊呆了,我咋就没考虑到是操作系统问题呢!知道了问题的原因以后,我和会军开始分别想从源头解决这个问题。但是半个小时过去了,发现要实现这个太难了,于是我提议放弃,就用我写的这个函数凑合着用。但是还是会军老道。虽然放弃了这步,但是最终还是通过修改转码那块的顺序搞定了这个问题!!!

      通过这次的编码训练又使我学习了很多东西,关于php,关于解决问题的思路。因为用了开源的php类,也参考了网上其它的同类文章,本着开源精神,现将全部代码整理如下:

<?php

include "php_serial.class.php";

//加载php操作串口的类
$serial = new phpSerial;

//连接USB gas modem
$serial->deviceSet("/dev/ttyUSB0");
$serial->deviceOpen();

//要发送的手机号:1531170xxxx
$phone_sendto = InvertNumbers('861531170xxxx');
//要发送的短信:I am rainbird,i love 中国
$message = hex2str('I am rainbird,i love 中国');
$mess = "11000D91".$phone_sendto."000800".sprintf("X",strlen($message)/2).$message;
$serial->sendMessage("at+cmgf=0".chr(13));
$serial->sendMessage("at+cmgs=".sprintf("%d",strlen($mess)/2).chr(13));
//不加短信中心号码
$serial->sendMessage('00'.$mess.chr(26));
//加短信中心号码
//$phone_center = InvertNumbers('8613800100500');
//$mess_ll = "0891".$phone_center.$mess;
//$serial->sendMessage($mess_ll.chr(26));

//用完了就关掉,有始有终好习惯
$serial->deviceClose();

//将utf8的短信转成ucs2格式
function hex2str($str) {
        $hexstring=iconv("UTF-8", "UCS-2", $str);
        $str = '';
        for($i=0; $i<strlen($hexstring)/2; $i++){
                $str .= sprintf("X",ord(substr($hexstring,$i*2+1,1)));
                $str .= sprintf("X",ord(substr($hexstring,$i*2,1)));
        }
        return $str;
}
//手机号翻转,pdu格式要求
function InvertNumbers($msisdn) {
        $len = strlen($msisdn);
        if ( 0 != fmod($len, 2) ) {
                $msisdn .= "F";
                $len = $len + 1;
        }

        for ($i=0; $i<$len; $i+=2) {
                $t = $msisdn[$i];
                $msisdn[$i] = $msisdn[$i+1];
                $msisdn[$i+1] = $t;
        }
        return $msisdn;
}
?>


其实如果只需要发送英文短信的话,只需要用php_serial。class连接modem并用at指令发短信就可以了。发送中文的时候才会用到pdu格式,当然用了pdu格式以后可以发混合的中英文。本文只作测试没有考虑短信最多只能发送74个汉字的情况,如需要请自行处理。PHP串口操作类php_serial

<?php
define ("SERIAL_DEVICE_NOTSET", 0);
define ("SERIAL_DEVICE_SET", 1);
define ("SERIAL_DEVICE_OPENED", 2);

class phpSerial
{
        var $_device = null;
        var $_windevice = null;
        var $_dHandle = null;
        var $_dState = SERIAL_DEVICE_NOTSET;
        var $_buffer = "";
        var $_os = "";

        var $autoflush = true;

        function phpSerial ()
        {
                setlocale(LC_ALL, "en_US");

                $sysname = php_uname();

                if (substr($sysname, 0, 5) === "Linux")
                {
                        $this->_os = "linux";

                        if($this->_exec("stty --version") === 0)
                        {
                                register_shutdown_function(array($this, "deviceClose"));
                        }
                        else
                        {
                                trigger_error("No stty availible, unable to run.", E_USER_ERROR);
                        }
                }
                elseif(substr($sysname, 0, 7) === "Windows")
                {
                        $this->_os = "windows";
                        register_shutdown_function(array($this, "deviceClose"));
                }
                else
                {
                        trigger_error("Host OS is neither linux nor windows, unable tu run.", E_USER_ERROR);
                        exit();
                }
        }

        function deviceSet ($device)
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        if ($this->_os === "linux")
                        {
                                if (preg_match("@^COM(\d+):?$@i", $device, $matches))
                                {
                                        $device = "/dev/ttyS" . ($matches[1] - 1);
                                }

                                if ($this->_exec("stty -F " . $device) === 0)
                                {
                                        $this->_device = $device;
                                        $this->_dState = SERIAL_DEVICE_SET;
                                        return true;
                                }
                        }
                        elseif ($this->_os === "windows")
                        {
                                if (preg_match("@^COM(\d+):?$@i", $device, $matches) and $this->_exec(exec("mode " . $device)) === 0)
                                {
                                        $this->_windevice = "COM" . $matches[1];
                                        $this->_device = "\\.\com" . $matches[1];
                                        $this->_dState = SERIAL_DEVICE_SET;
                                        return true;
                                }
                        }

                        trigger_error("Specified serial port is not valid", E_USER_WARNING);
                        return false;
                }
                else
                {
                        trigger_error("You must close your device before to set an other one", E_USER_WARNING);
                        return false;
                }
        }

        function deviceOpen ($mode = "r+b")
        {
                if ($this->_dState === SERIAL_DEVICE_OPENED)
                {
                        trigger_error("The device is already opened", E_USER_NOTICE);
                        return true;
                }

                if ($this->_dState === SERIAL_DEVICE_NOTSET)
                {
                        trigger_error("The device must be set before to be open", E_USER_WARNING);
                        return false;
                }

                if (!preg_match("@^[raw]\+?b?$@", $mode))
                {
                        trigger_error("Invalid opening mode : ".$mode.". Use fopen() modes.", E_USER_WARNING);
                        return false;
                }

                $this->_dHandle = @fopen($this->_device, $mode);

                if ($this->_dHandle !== false)
                {
                        stream_set_blocking($this->_dHandle, 0);
                        $this->_dState = SERIAL_DEVICE_OPENED;
                        return true;
                }

                $this->_dHandle = null;
                trigger_error("Unable to open the device", E_USER_WARNING);
                return false;
        }

        function deviceClose ()
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        return true;
                }

                if (fclose($this->_dHandle))
                {
                        $this->_dHandle = null;
                        $this->_dState = SERIAL_DEVICE_SET;
                        return true;
                }

                trigger_error("Unable to close the device", E_USER_ERROR);
                return false;
        }

        function confBaudRate ($rate)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set the baud rate : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $validBauds = array (
                        110    => 11,
                        150    => 15,
                        300    => 30,
                        600    => 60,
                        1200   => 12,
                        2400   => 24,
                        4800   => 48,
                        9600   => 96,
                        19200  => 19,
                        38400  => 38400,
                        57600  => 57600,
                        115200 => 115200
                );

                if (isset($validBauds[$rate]))
                {
                        if ($this->_os === "linux")
                        {
                                $ret = $this->_exec("stty -F " . $this->_device . " " . (int) $rate, $out);
                        }
                        elseif ($this->_os === "windows")
                        {
                                $ret = $this->_exec("mode " . $this->_windevice . " BAUD=" . $validBauds[$rate], $out);
                        }
                        else return false;

                        if ($ret !== 0)
                        {
                                trigger_error ("Unable to set baud rate: " . $out[1], E_USER_WARNING);
                                return false;
                        }
                }
        }

        function confParity ($parity)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set parity : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $args = array(
                        "none" => "-parenb",
                        "odd"  => "parenb parodd",
                        "even" => "parenb -parodd",
                );

                if (!isset($args[$parity]))
                {
                        trigger_error("Parity mode not supported", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " " . $args[$parity], $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " PARITY=" . $parity{0}, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set parity : " . $out[1], E_USER_WARNING);
                return false;
        }

        function confCharacterLength ($int)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set length of a character : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $int = (int) $int;
                if ($int < 5) $int = 5;
                elseif ($int > 8) $int = 8;

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " cs" . $int, $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " DATA=" . $int, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set character length : " .$out[1], E_USER_WARNING);
                return false;
        }

        function confStopBits ($length)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set the length of a stop bit : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                if ($length != 1 and $length != 2 and $length != 1.5 and !($length == 1.5 and $this->_os === "linux"))
                {
                        trigger_error("Specified stop bit length is invalid", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux")
                {
                        $ret = $this->_exec("stty -F " . $this->_device . " " . (($length == 1) ? "-" : "") . "cstopb", $out);
                }
                else
                {
                        $ret = $this->_exec("mode " . $this->_windevice . " STOP=" . $length, $out);
                }

                if ($ret === 0)
                {
                        return true;
                }

                trigger_error("Unable to set stop bit length : " . $out[1], E_USER_WARNING);
                return false;
        }

        function confFlowControl ($mode)
        {
                if ($this->_dState !== SERIAL_DEVICE_SET)
                {
                        trigger_error("Unable to set flow control mode : the device is either not set or opened", E_USER_WARNING);
                        return false;
                }

                $linuxModes = array(
                        "none"     => "clocal -crtscts -ixon -ixoff",
                        "rts/cts"  => "-clocal crtscts -ixon -ixoff",
                        "xon/xoff" => "-clocal -crtscts ixon ixoff"
                );
                $windowsModes = array(
                        "none"     => "xon=off octs=off rts=on",
                        "rts/cts"  => "xon=off octs=on rts=hs",
                        "xon/xoff" => "xon=on octs=off rts=on",
                );

                if ($mode !== "none" and $mode !== "rts/cts" and $mode !== "xon/xoff") {
                        trigger_error("Invalid flow control mode specified", E_USER_ERROR);
                        return false;
                }

                if ($this->_os === "linux")
                        $ret = $this->_exec("stty -F " . $this->_device . " " . $linuxModes[$mode], $out);
                else
                        $ret = $this->_exec("mode " . $this->_windevice . " " . $windowsModes[$mode], $out);

                if ($ret === 0) return true;
                else {
                        trigger_error("Unable to set flow control : " . $out[1], E_USER_ERROR);
                        return false;
                }
        }

        function setSetserialFlag ($param, $arg = "")
        {
                if (!$this->_ckOpened()) return false;

                $return = exec ("setserial " . $this->_device . " " . $param . " " . $arg . " 2>&1");

                if ($return{0} === "I")
                {
                        trigger_error("setserial: Invalid flag", E_USER_WARNING);
                        return false;
                }
                elseif ($return{0} === "/")
                {
                        trigger_error("setserial: Error with device file", E_USER_WARNING);
                        return false;
                }
                else
                {
                        return true;
                }
        }

        function sendMessage ($str, $waitForReply = 0.1)
        {
                $this->_buffer .= $str;

                if ($this->autoflush === true) $this->flush();

                usleep((int) ($waitForReply * 1000000));
        }

        function readPort ($count = 0)
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        trigger_error("Device must be opened to read it", E_USER_WARNING);
                        return false;
                }

                if ($this->_os === "linux")
                {
                        $content = ""; $i = 0;

                        if ($count !== 0)
                        {
                                do {
                                        if ($i > $count) $content .= fread($this->_dHandle, ($count - $i));
                                        else $content .= fread($this->_dHandle, 128);
                                } while (($i += 128) === strlen($content));
                        }
                        else
                        {
                                do {
                                        $content .= fread($this->_dHandle, 128);
                                } while (($i += 128) === strlen($content));
                        }

                        return $content;
                }
                elseif ($this->_os === "windows")
                {
                        /* Do nohting : not implented yet */
                }

                trigger_error("Reading serial port is not implemented for Windows", E_USER_WARNING);
                return false;
        }

        function flush ()
        {
                if (!$this->_ckOpened()) return false;

                if (fwrite($this->_dHandle, $this->_buffer) !== false)
                {
                        $this->_buffer = "";
                        return true;
                }
                else
                {
                        $this->_buffer = "";
                        trigger_error("Error while sending message", E_USER_WARNING);
                        return false;
                }
        }


        function _ckOpened()
        {
                if ($this->_dState !== SERIAL_DEVICE_OPENED)
                {
                        trigger_error("Device must be opened", E_USER_WARNING);
                        return false;
                }

                return true;
        }

        function _ckClosed()
        {
                if ($this->_dState !== SERIAL_DEVICE_CLOSED)
                {
                        trigger_error("Device must be closed", E_USER_WARNING);
                        return false;
                }

                return true;
        }

        function _exec($cmd, &$out = null)
        {
                $desc = array(
                        1 => array("pipe", "w"),
                        2 => array("pipe", "w")
                );

                $proc = proc_open($cmd, $desc, $pipes);

                $ret = stream_get_contents($pipes[1]);
                $err = stream_get_contents($pipes[2]);

                fclose($pipes[1]);
                fclose($pipes[2]);

                $retVal = proc_close($proc);

                if (func_num_args() == 2) $out = array($ret, $err);
                return $retVal;
        }

}
?>

本文转自雷田博客:http://www.23to.com/technical/37.html

短信设备二次开发 2022-04-06 0 0

admin

  • 注册时间 : 2022-03-30 04:08:47
  • 邮箱地址 : admin@tenghengkeji.com
  • 此页面用于展示用户的基本上资料信息

回帖 ( 0 )