<?php
// vim: set expandtab cindent tabstop=4 shiftwidth=4 fdm=marker:
// +----------------------------------------------------------------------+
// | The Function Inc                                                     |
// +----------------------------------------------------------------------+
// | Copyright (c) 2014, Function Inc. All rights reserved.               |
// +----------------------------------------------------------------------+
// | Authors: The PHP Dev Team, ISRD, Function Inc.                       |
// |          sniferyuan <sniferyuan@gmail.com>                           |
// +----------------------------------------------------------------------+

/**
 * 封装一些常用的网络操作函数
 */
abstract class NetUtil
{
    /**
     * 错误编码
     */
    public static $errCode = 0;
    /**
     * 错误信息,无错误为''
     */
    public static $errMsg  = '';

    /**
     * 清除错误信息,在每个函数的开始调用
     */
    private static function clearError()
    {
        self::$errCode = 0;
        self::$errMsg    = '';
    }

    /**
     * 对socket_read的封装,支持多个包的传播,此函数针对TcpServer的
     * 前8个字节为消息的长度
     * 接下来的4个字节为错误编码
     * 接下来的是正文
     * @param socket      socket句柄
     * @param int maxLength   能接收数据的字符串长度
     *
     * @return string 正确返回读取的数据,错误返回false
     */
    public static function tcpSocketRead(&$socket, $maxLength)
    {
        self::clearError();
        $str = @socket_read($socket, 10240);
        if ($str === false){
            self::$errCode = 10102;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            return false;
        }
        if (strlen($str) < 8) {
            self::$errCode = 10102;
            self::$errMsg  = 'bad tcp bag';
            return false;
        }
        $len = trim(substr($str, 0, 8));
        if(!is_numeric($len)){
            self::$errCode = 10104;
            self::$errMsg  = 'bad tcp bag';
            return false;
        }
        if ($len > $maxLength) {
            self::$errCode = 10105;
            self::$errMsg  = 'tcp bag too big';
            return false;
        }
        $message = substr($str, 8);
        $n = strlen($message);
        if ($n == $len) {
            return $message;
        }
        if ($n > $len) {
            self::$errCode = 10104;
            self::$errMsg  = 'bad tcp bag';
            return false;
        }

        while ($len > $n)
        {
            $tmp = @socket_read($socket, 10240);
            if ($tmp === false) {
                self::$errCode = 10102;
                self::$errMsg  = @socket_strerror(@socket_last_error($socket));
                return false;
            }
            $message .= $tmp;
            $n = strlen($message);
            unset($tmp);
        }
        if ($n != $len) {
            self::$errCode = 10105;
            self::$errMsg  = 'bad tcp bag';
            return false;
        }
        return $message;
    }

    /**
     * 对socket_write的封装,支持多个包的传播,此函数针对TcpServer的
     *
     * @param socket    socket句柄
     * @param string message    需要发送的消息
     *
     * @return bool 正确返回true,错误返回false
     */
    public static function tcpSocketWrite(&$socket, $message)
    {
        self::clearError();
        $len     = strlen($message);
        $padStr  = str_pad($len, 8, ' ', STR_PAD_RIGHT);
        $message = $padStr.$message;
        $len = $len + 8;
        $n = @socket_write($socket, $message, $len);
        if($n === false) {
            self::$errCode = 10103;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            return false;
        }
        while ($n < $len) {
            $tmp   = substr($message, $n);
            $tmp_n = @socket_write($socket, $tmp, ($len - $n));
            if ($tmp_n === false) {
                self::$errCode = 10103;
                self::$errMsg  = @socket_strerror(@socket_last_error($socket));
                return false;
            }
            $n += $tmp_n;
        }
        if ($n == $len) {
            return true;
        }
        return false;
    }

    /**
     * 处理简单的tcp发包收包,只适合短包的操作,最大10k
     * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
     *
     * @param string ip        IP地址
     * @param int port      端口
     * @param string cmd       向server发送命令
     * @param int n         错误重试次数
     * @param int timeout_sec      超时秒
     * @param int timeout_usec     超时u秒
     *
     * @return string 错误返回false,正确返回收到的信息
     */
    public static function tcpCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
    {
        self::clearError();
        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!$socket) {
            self::$errCode = 10101;
            self::$errMsg  = @socket_strerror(@socket_last_error());
            oo::logs()->debug3(10101, 'tcpcmd.log');
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            oo::logs()->debug3(10106, 'tcpcmd.log');
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            oo::logs()->debug3(10106, 'tcpcmd.log');
            return false;
        }
        $ret = false;
        for ($i = 0; $i < $n; $i++){
            $ret = @socket_connect($socket, $ip, $port);
            if ($ret == true) break;
        }
        if ($ret === false) {
            self::$errCode = 10107;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            oo::logs()->debug3(10107, 'tcpcmd.log');
            return false;
        }
        $len = strlen($cmd);
        $n = 0;
        $tmp = $cmd;
        while ($n < $len){
            $ret = @socket_write($socket, $tmp, $len);
            if ($ret == false) {
                self::$errCode = 10103;
                self::$errMsg  = @socket_strerror(@socket_last_error($socket));
                @socket_close($socket);
                oo::logs()->debug3(10103, 'tcpcmd.log');
                return false;
            }
            $n += $ret;
            if ($n < $len) {
                $tmp = substr($tmp, $ret);
            }
        }
        $rev = @socket_read($socket, 10240);
        if ($rev == false) {
            self::$errCode = 10102;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            oo::logs()->debug3(10102, 'tcpcmd.log');
        }

        @socket_close($socket);
        return $rev;
    }


    /**
     * 处理简单的tcp发包,不收包
     * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
     *
     * @param string ip        IP地址
     * @param int port      端口
     * @param string cmd       向server发送命令
     * @param int n         错误重试次数
     * @param int timeout_sec      超时秒
     * @param int timeout_usec     超时u秒
     *
     * @return string 错误返回false,正确返回收到的信息
     */
    public static function tcpCmdNotRecv($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
    {
        self::clearError();
        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!$socket) {
            self::$errCode = 10101;
            self::$errMsg  = @socket_strerror(@socket_last_error());
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $ret = false;
        for ($i = 0; $i < $n; $i++){
            $ret = @socket_connect($socket, $ip, $port);
            if ($ret == true) break;
        }
        if ($ret === false) {
            self::$errCode = 10107;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $len = strlen($cmd);
        $n = 0;
        $tmp = $cmd;
        while ($n < $len){
            $ret = @socket_write($socket, $tmp, $len);
            if ($ret == false) {
                self::$errCode = 10103;
                self::$errMsg  = @socket_strerror(@socket_last_error($socket));
                @socket_close($socket);
                return false;
            }
            $n += $ret;
            if ($n < $len) {
                $tmp = substr($tmp, $ret);
            }
        }

        @socket_close($socket);
        return true;
    }


    /**
     * 处理简单的tcp发包收包,只适合短包的操作,最大10k
     * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
     *
     * @param string ip        IP地址
     * @param int port      端口
     * @param string cmd       向server发送命令
     * @param int n         错误重试次数
     * @param int timeout_sec      超时秒
     * @param int timeout_usec     超时u秒
     * @param string end           结束符
     * @param bool flag            true表示还要收返回包，false不用收
     *
     * @return string 错误返回false,正确返回收到的信息
     */
    public static function tcpLongCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0, $end = "\0", $flag = true )
    {
        self::clearError();
        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!$socket) {
            self::$errCode = 10101;
            self::$errMsg  = @socket_strerror(@socket_last_error());
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $ret = false;
        for ($i = 0; $i < $n; $i++){
            $ret = @socket_connect($socket, $ip, $port);
            if ($ret == true) break;
        }
        if ($ret === false) {
            self::$errCode = 10107;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $len = strlen($cmd);
        $n = 0;
        $tmp = $cmd;
        while ($n < $len){
            $ret = @socket_write($socket, $tmp, $len);
            if ($ret == false) {
                self::$errCode = 10103;
                self::$errMsg  = @socket_strerror(@socket_last_error($socket));
                @socket_close($socket);
                return false;
            }
            $n += $ret;
            if ($n < $len) {
                $tmp = substr($tmp, $ret);
            }
        }

        $tmp = true;
        $rev = "";
        if ( $flag )
        {
            while ( !empty( $tmp ) )
            {
                $tmp = socket_read($socket, 1024);
                if ( empty( $tmp ) )
                {
                    break;
                }
                $rev = $rev.$tmp;
                if ( $tmp[strlen($tmp) - 1] == $end )
                {
                    break;
                }
            }
        }
        @socket_close($socket);
        return $rev;
    }


    /**
     * 对socket_recvfrom的封装,支持对包的校验,此函数针对udpServer的
     *
     * @param socket    socket句柄
     * @param string message    需要发送的消息
     * @param string ip ip地址
     * @param int port  端口
     *
     * @return bool 正确返回true,错误返回false
     */
    public static function udpSocketRecvFrom(&$socket, $maxLength, &$ip, &$port)
    {
        self::clearError();
        $message = '';
        $n = @socket_recvfrom($socket, $message, $maxLength, 0, $ip, $port);
        if ($n === false){
            self::$errCode = 10104;
            self::$errMsg  = @socket_strerror(@socket_last_error());
            return false;
        }
        if (strlen($message) < 8 || $n < 8) {
            self::$errCode = 10104;
            self::$errMsg  = 'bad udp bag';
            return false;
        }
        $len = trim(substr($message, 0, 8));
        if(!is_numeric($len))
        {
            self::$errCode = 10104;
            self::$errMsg  = 'bad udp bag';
            return false;
        }
        if ($len > $maxLength) {
            self::$errCode = 10105;
            self::$errMsg  = 'udp bag too big';
            return false;
        }
        $message = substr($message, 8);
        $n = strlen($message);
        if ($n == $len) {
            return $message;
        }
        self::$errCode = 10104;
        self::$errMsg  = 'bad udp bag';
        return false;
    }

    /**
     * 对socket_sendto的封装,支持对包的校验,此函数针对udpServer的,加8个字符的长度
     *
     * @param socket    socket句柄
     * @param string message    需要发送的消息
     * @param string ip ip地址
     * @param int port  端口
     *
     * @return bool 正确返回true,错误返回false
     */
    public static function udpSocketSendTo(&$socket, $message, $ip, $port)
    {
        self::clearError();
        $len = strlen($message);
        $padStr  = str_pad($len, 8, ' ', STR_PAD_RIGHT);
        $message = $padStr.$message;
        $len += 8;
        $n = @socket_sendto($socket, $message, $len, 0, $ip, $port);
        if ($n === $len) {
            self::$errCode = 10103;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            return true;
        }
        self::$errCode = 10103;
        self::$errMsg ='udp send error';
        return false;
    }
    /**
     * 正确返回接受到的数据,错误返回false
     *
     * @param string ip    ip地址
     * @param int port    端口
     * @param string cmd    命令字符串
     * @param boolean isResponse    是否需要回复
     * @param int timeout    超时时间
     */
    public static function udpCmd($ip, $port, $cmd, $isResponse=true, $timeout = 2, $utmo = 0)
    {
        self::clearError();
        $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        if (!$socket) {
            self::$errCode = 10101;
            self::$errMsg = @socket_strerror(@socket_last_error());
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $n = @socket_sendto($socket, $cmd, 10240, 0, $ip, $port);
        if ($n == -1) {
            self::$errCode = 10103;
            self::$errMsg = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        if ($isResponse === false) {
            return true;
        }

        $revBuf = '';
        $ret = @socket_recvfrom($socket, $revBuf, 10240, 0, $ip, $port);
        if ($ret == -1) {
            self::$errCode = 10102;
            self::$errMsg = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        return $revBuf;
    }

    /**
     * 正确返回接受到的数据,错误返回false
     *
     * @param string ip    ip地址
     * @param int port    端口
     * @param string cmd    命令字符串
     * @param boolean isResponse    是否需要回复
     * @param int timeout    超时时间
     */
    public static function udpPHPCmd($ip, $port, $cmd, $isResponse=true, $timeout = 2, $utmo = 0)
    {
        self::clearError();
        $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        if (!$socket) {
            self::$errCode = 10101;
            self::$errMsg  = @socket_strerror(@socket_last_error());
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
            self::$errCode = 10106;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket));
            @socket_close($socket);
            return false;
        }
        $n = self::udpSocketSendTo($socket, $cmd, $ip, $port);
        if ($n == false) {
            @socket_close($socket);
            return false;
        }
        if ($isResponse === false) {
            return true;
        }
        return self::udpSocketRecvFrom($socket, 10240, $ip, $port);
    }

    /**
     * TcpServer的客户端
     *
     * @param string ip      ip地址
     * @param int     port    端口
     * @param string cmd     命令字符串
     * @param int timeout_sec    超时秒
     * @param int timeout_usec   超时u秒
     *
     * @return  string 正确返回接受到的数据,错误返回false
     */
    public static function tcpPHPCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
    {
        self::clearError();
        $hostInfo = " to {$ip}:{$port} ";    // 连接的 ip:port 信息
        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if(!$socket) {
            self::$errCode = 10101;
            self::$errMsg  = @socket_strerror(@socket_last_error()) . $hostInfo;
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
            @socket_close($socket);
            return false;
        }
        if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
            self::$errCode = 10106;
            self::$errMsg = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
            @socket_close($socket);
            return false;
        }
        $ret = false;
        for ($i = 0; $i < $n; $i++){
            $ret = @socket_connect($socket, $ip, $port);
            if ($ret == true) break;
        }
        if ($ret === false) {
            self::$errCode = 10107;
            self::$errMsg  = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
            @socket_close($socket);
            return false;
        }
        $ret = self::tcpSocketWrite($socket, $cmd);
        if ( $ret===false ) {
            @socket_close($socket);
            return false;
        }
        $rev = self::tcpSocketRead($socket, 1024000);
        @socket_close($socket);
        return $rev;
    }

    /**
     * 常用打包函数
     *
     * @author    hickwu
     * @param    mix        $data    需要打包的数据
     * @return    string
     */
    public static function wrap($data) {
        $str = serialize($data);

        return $str;
    }

    /**
     * 常用解包函数
     *
     * @author    hickwu
     * @param    string        $str    需要解包的数据
     * @return    mix            解包失败返回 false，成功返回打包数据
     */
    public static function unwrap($str)
    {
        self::clearError();

        $arr = unserialize($str);

        if ($arr === false) {
            self::$errCode = 10613;
            self::$errMsg  = 'unserialize-err-' . serialize($str);
        }

        return $arr;
    }

    // ##################### Qzone 通用协议相关函数 ####################

    /**
     * 求二进制串校验和
     *
     * @author        peterdu
     *
     * @param        string    $bytes, 二进制串
     *
     * @return        short    $sum, 校验和值
     */
    public static function checkSum($bytes) {
        self::clearError();

        $len = strlen($bytes);

        $sum = 0;
        $tail = 0;

        if ( $len % 2 )
        {
            $tail = ord( substr($bytes, -1) );
        }

        $len = (int)($len / 2);

        for ($i = 0; $i < $len; $i++)
        {
            $chunk = substr($bytes, $i * 2, 2);

            $sum += array_pop( unpack('S', $chunk) );
        }

        $sum += $tail;

        $sum = ($sum >> 16) + ($sum & 0xffff);
        $sum += ($sum >> 16);

        return hexdec( substr( dechex(~$sum), 4 ) );
    }

    /**
     * 构建 Qzone 通用协议包
     *
     * @author        peterdu
     *
     * @param        int            $ver, 协议版本(注意: 通用协议源文件中, 此处传入为字符类型, php 中调用需传入其 ascii 码)
     * @param        int            $cmd, 协议命令字
     * @param        string        $body, 协议包体
     * @param        bool        $chk, 是否需要校验和, 可选参数, 默认为 false
     * @param        int            $sn, 协议序列号, 可选参数, 默认为0
     * @param        int            $color, 染色信息, 可选信息, 默认为0
     *
     * @return        string        $pack_str, 构建好的协议包
     */
    public static function structQzoneCmmPrtcl($ver, $cmd, $body, $chk = false, $sn = 0, $color = 0) {
        self::clearError();

        // 协议头标识字符
        $pre_ch = chr(4);

        // 协议版本号
        $ver = intval($ver);
        $ver = chr($ver);

        // 协议命令字
        $cmd = intval($cmd);
        $cmd = pack('N', $cmd);

        // 初始化校验和
        $init_checksum = pack('n', 0);

        // 协议序列号
        $sn = intval($sn);
        $sn = pack('N', $sn);

        // 协议染色信息
        $color = intval($color);
        $color = pack('N', $color);

        // 回应标识
        $respflag = chr(100);

        // 回应信息
        $respinfo = pack('n', 0);

        // 保留字段
        $reserve = chr(0);

        // 协议包长度
        $head_len = 25;
        $body_len = strlen($body);
        $pack_len = pack('N', $head_len + $body_len);

        // 协议尾标识字符
        $tail_ch = chr(5);

        $pack_str_1 = '';
        $pack_str_2 = '';

        $pack_str_1 .= $pre_ch;
        $pack_str_1 .= $ver;
        $pack_str_1 .= $cmd;

        $pack_str_2 .= $sn;
        $pack_str_2 .= $color;
        $pack_str_2 .= $respflag;
        $pack_str_2 .= $respinfo;
        $pack_str_2 .= $reserve;
        $pack_str_2 .= $pack_len;
        $pack_str_2 .= $body;
        $pack_str_2 .= $tail_ch;

        $pack_str = $pack_str_1 . $init_checksum . $pack_str_2;

        if ( $chk ) {
            // 计算实际校验和
            $act_checksum = self::checkSum($pack_str);
            $act_checksum = pack('s', $act_checksum);

            $pack_str = $pack_str_1 . $act_checksum . $pack_str_2;
        }

        return $pack_str;
    }

    /**
     * 解析 Qzone 通用协议包
     *
     * @author        peterdu
     *
     * @param        string            $pack_str, 协议包(二进制串)
     * @param        bool            $chk, 是否检测校验和, 可选参数, 默认为 false
     *
     * @return        array/bool        $pack, 为协议包解析后的数组, 元素包括协议版本, 协议命令字, 回应标识, 协议体等, 错误返回 false
     */
    public static function parseQzoneCmmPrtcl($pack_str, $chk = false) {
        self::clearError();

        $data = array();
        $data['errcode'] = 0;
        $data['errmsg'] = '';

        $pack_len = strlen($pack_str);

        if ( !$pack_len ) {
            self::$errCode = 10612;
            self::$errMsg = 'empty package';

            return false;
        }

        // 若检测校验和
        if ( $chk ) {
            $chksum = self::checkSum($pack_str);

            if ( $chksum != 0x0 && $chksum != 0xffff )
            {
                self::$errCode = 10613;
                self::$errMsg = 'validate checksum failed';

                return false;
            }
        }

        // 均转换为10进制, 避免使用 unpack 引起符号问题
        $ver        = hexdec( bin2hex( substr($pack_str, 1, 1) ) );
        $cmd        = hexdec( bin2hex( substr($pack_str, 2, 4) ) );
        $checksum    = hexdec( bin2hex( substr($pack_str, 6, 2) ) );
        $sn            = hexdec( bin2hex( substr($pack_str, 8, 4) ) );
        $color        = hexdec( bin2hex( substr($pack_str, 12, 4) ) );
        $respflag    = hexdec( bin2hex( substr($pack_str, 16, 1) ) );
        $respinfo    = hexdec( bin2hex( substr($pack_str, 17, 2) ) );
        $packlen    = hexdec( bin2hex( substr($pack_str, 20, 4) ) );

        if ( $packlen != $pack_len ) {
            self::$errCode = 10614;
            self::$errMsg = 'package length is not match';

            return false;
        }

        // 截取包体
        $body        = substr($pack_str, 24, -1);

        $pack                = array();
        $pack['ver']        = $ver;
        $pack['cmd']        = $cmd;
        $pack['checksum']    = $checksum;
        $pack['sn']            = $sn;
        $pack['color']        = $color;
        $pack['respflag']    = $respflag;
        $pack['respinfo']    = $respinfo;
        $pack['packlen']    = $packlen;
        $pack['body']        = $body;

        return $pack;
    }

    // ##################### cURL 请求相关函数 ####################

    /**
     * 使用 cURL 实现 HTTP GET 请求
     *
     * @param        string            $url, 请求地址
     * @param        string            $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
     * @param        int                $timeout, 连接超时时间, 默认为2
     *
     * @return        sting/bool        $data, 为返回数据, 失败返回 false
     */
    public static function cURLHTTPGet($url, $timeout = 2, $host = '', $cookie = '', $exectime = 3) {
        self::clearError();

        $header = array('Content-transfer-encoding: text');

        if ( !empty($host) ) {
            $header[] = 'Host: ' . $host;
        }

        if ( defined( 'SITE_DOMAIN' ) &&  in_array(SITE_DOMAIN, array('renren') ) ) {
            $real_user_ip = $_SERVER['REMOTE_ADDR'] ;
        } else {
            $real_user_ip = defined( 'USER_REAL_IP' ) ? USER_REAL_IP : $_SERVER['REMOTE_ADDR'] ;
        }

        $header[] = 'X-FORWARDED-FOR: '.$real_user_ip ;


        $curl_handle = curl_init();

        // 连接超时
        curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
        // 执行超时
        curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
        // HTTP返回错误时, 函数直接返回错误
        curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
        // 允许重定向
        curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
        // 允许重定向的最大次数
        curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
        // 返回为字符串
        curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
        // 设置HTTP头
        curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $header);
        // 指定请求地址
        curl_setopt($curl_handle, CURLOPT_URL, $url);
        // 设置cookie
        curl_setopt($curl_handle, CURLOPT_COOKIE, $cookie) ;



        // 执行请求
        $response = curl_exec($curl_handle);

        if ( $response === false ) {
            self::$errCode = 10615;
            self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
            // 关闭连接
            curl_close($curl_handle);

            return false;
        }

        // 关闭连接
        curl_close($curl_handle);

        return $response;
    }


        /**
     * 使用 cURL 实现 服务器HTTP GET 请求   无header
     *
     * @param        string            $url, 请求地址
     * @param        string            $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
     * @param        int                $timeout, 连接超时时间, 默认为2
     * @return        sting/bool        $data, 为返回数据, 失败返回 false
     */
    public static function cURLHTTPGetNormal($url, $timeout = 2, $exectime = 3) {

        self::clearError();
        $curl_handle = curl_init();
        // 连接超时
        curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
        // 执行超时
        curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
        // HTTP返回错误时, 函数直接返回错误
        curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
        // 允许重定向
        curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
        // 允许重定向的最大次数
        curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
        // 返回为字符串
        curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
        // 指定请求地址
        curl_setopt($curl_handle, CURLOPT_URL, $url);

        // 执行请求
        $response = curl_exec($curl_handle);

        if ( $response === false ) {
            self::$errCode = 10615;
            self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
            // 关闭连接
            curl_close($curl_handle);

            return false;
        }

        // 关闭连接
        curl_close($curl_handle);

        return $response;
    }

    /**
     * 使用 cURL 实现 HTTP POST 请求
     *
     * @param        string            $url, 请求地址
     * @param        string            $post_data, 请求的post数据，一般为经过urlencode 和用&处理后的字符串
     * @param        string            $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
     * @param        int                $timeout, 连接超时时间, 默认为2
     *
     * @return        sting/bool        $data, 为返回数据, 失败返回 false
     */
    public static function cURLHTTPPost($url, $post_data, $timeout = 2, $host = '', $cookie = '', $exectime = 3) {
        self::clearError();

        $header = array('Content-transfer-encoding: text');
        if (is_string($post_data)) {
            $data_len = strlen($post_data);
            $header[] = 'Content-Length: ' . $data_len;
        }

        if ( !empty($host) ) {
            $header[] = 'Host: ' . $host;
        }

        $real_user_ip = defined( 'USER_REAL_IP' ) ? USER_REAL_IP : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '');
        $header[] = 'X-FORWARDED-FOR: '.$real_user_ip ;

        $curl_handle = curl_init();

        // 连接超时
        curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
        // 执行超时
        curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
        // HTTP返回错误时, 函数直接返回错误
        curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
        // 允许重定向
        curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
        // 允许重定向的最大次数
        curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
        // 返回为字符串
        curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
        // 设置HTTP头
        curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $header);
        // 指定请求地址
        curl_setopt($curl_handle, CURLOPT_URL, $url);
        //设置为post方式
        curl_setopt($curl_handle, CURLOPT_POST, TRUE);
        //post 参数
        curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $post_data);
        // 设置cookie
        curl_setopt($curl_handle, CURLOPT_COOKIE, $cookie) ;
        // 执行请求
        $response = curl_exec($curl_handle);

        if ( $response === false ) {
            self::$errCode = 10616;
            self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
            // 关闭连接
            curl_close($curl_handle);

            return false;
        }

        // 关闭连接
        curl_close($curl_handle);

        return $response;
    }


    /**
     * 维护分表统一自增字段
     *
     * @param Mixed $code
     * @return 获取的可插入DB的ID
     */
    public static function getAutoId($code){
        self::clearError();
        if(empty($code) || !is_numeric($code)){
            self::$errCode = 5001;
            self::$errMsg = 'autoid code err';
            return false;
        }

        $autoIdSvr = Config::getIP('autoId');
        if($autoIdSvr === false){
            self::$errCode = Config::$errCode;
            self::$errMsg = Config::$errMsg;
            return false;
        }

        $bag = array(
                    'code'=>intval($code)
                );
        $bag = self::wrap($bag);
        $rev = self::tcpPHPCmd($autoIdSvr['IP'], $autoIdSvr['PORT'], $bag);
        if($rev === false){
            self::$errCode = self::$errCode;
            self::$errMsg = self::$errMsg;
            return false;
        }

        $rev = self::unwrap($rev);

        if(!is_array($rev)){
            self::$errCode = 5002;
            self::$errMsg = 'server returns errno ' . $rev;
            return false;
        }

        if(!isset($rev['value'])){
            self::$errCode = 5003;
            self::$errMsg = 'server returns no value';
            return false;
        }

        return intval($rev['value']);
    }


}

//End of script

?>
