欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

php tcp udp 通信

程序员文章站 2022-06-06 16:32:50
...
 /**
     * udp收发包
     * @param $ip
     * @param $port
     * @param $timeout
     * @return int 0-成功,非0-失败(具体参考类头部错误码常量定义)
     */
    private function udpSocket($ip, $port, $timeout)
    {
        $time = microtime(true);
        $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);

        if (false === $sock) {
            return self::UDL_SOCKET_CREATE_FAILED; // socket创建失败
        }

        if (!socket_set_nonblock($sock)) {
            return self::UDL_SOCKET_SET_NONBLOCK_FAILED; // 设置socket非阻塞失败
        }

        $len = strlen($this->mUdlRequestBuf);
        if (socket_sendto($sock, $this->mUdlRequestBuf, $len, 0x100, $ip, $port) != $len) {
            return self::UDL_SOCKET_SEND_FAILED; // socket发送失败
        }

        if (0 == $timeout) {
            return self::UDL_SUCCESS; // 无回包的情况,返回成功
        }

        $read = array($sock);
        $second = floor($timeout);
        $usecond = ($timeout - $second) * 1000000;
        $ret = socket_select($read, $write, $except, $second, $usecond);

        if (FALSE === $ret) {
            return self::UDL_SOCKET_RECEIVE_FAILED; // 收包失败
        } elseif ($ret != 1) {
            return self::UDL_SOCKET_SELECT_TIMEOUT; // 收包超时
        }

        $out = null;
        while (true) {
            if (microtime(true) - $time > $timeout) {
                return self::UDL_SOCKET_TIMEOUT; // 收包超时
            }

            // 32k:32768 = 1024 * 32
            $outLen = @socket_recvfrom($sock, $out, 32768, 0, $host, $port);
            if (!($outLen > 0 && $out != '')) {
                continue;
            }

            $list = unpack('Nseq', substr($out, 9, 4));
            $seq = $list['seq'];

            if ($seq !== $this->mSeq) {
                continue;
            }

            $this->mUdlResponseBuf = $out;

            return self::UDL_SUCCESS;
        }
    }

    /**
     * udp收发包
     * @param $ip
     * @param $port
     * @param $timeout
     * @return int 0-成功,非0-失败(具体参考类头部错误码常量定义)
     */
    private function tcpSocket($ip, $port, $timeout)
    {
        $time = microtime(true);
        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        if (false === $sock) {
            return self::UDL_SOCKET_CREATE_FAILED; // socket创建失败
        }

        if (!socket_connect($sock, $ip, $port)) {
            return self::UDL_SOCKET_CONNECT_FAILED;
        }

        $len = strlen($this->mUdlRequestBuf);
        if (socket_write($sock, $this->mUdlRequestBuf, $len) != $len) {
            return self::UDL_SOCKET_SEND_FAILED;
        }

        $read = array($sock);
        $ret = socket_select($read, $write = null, $except = null, $timeout);

        if (false === $ret) {
            return self::UDL_SOCKET_RECEIVE_FAILED;
        } elseif ($ret != 1) {
            return self::UDL_SOCKET_SELECT_TIMEOUT;
        }

        $totalLen = 0;
        while (true) {
            if (microtime(true) - $time > $timeout) {
                return self::UDL_SOCKET_TIMEOUT; // 收包超时
            }

            //读取最多32M的数据
            $data = socket_read($sock, self::SOCKET_TCP_MAX_PCK_SIZE, PHP_BINARY_READ);

            if (empty($data)) {
                // 已经断开连接
                return self::UDL_SOCKET_CLOSED;
            } else {
                //第一个包
                if ($this->mUdlResponseBuf === null) {
                    $this->mUdlResponseBuf = $data;

                    //在这里从第一个包中获取总包长
                    $list = unpack('Nlen', substr($data, 1, 4));
                    $totalLen = $list['len'];
                } else {
                    $this->mUdlResponseBuf .= $data;
                }

                //check if all package is receved
                if (strlen($this->mUdlResponseBuf) >= $totalLen) {
                    return self::UDL_SUCCESS;
                }
            }
        }
    }

    /**
     * 打包、收发包、解包
     * @param $ip
     * @param $port
     * @param int $timeout
     * @return int
     */
    private function sendAndReceive($ip, $port, $timeout = 2, $socketMode)
    {
        try {
            $this->serialize();

            if ($socketMode === self::SOCKET_MODE_UDP) {
                $r = $this->udpSocket($ip, $port, $timeout);
            } else {
                $r = $this->tcpSocket($ip, $port, $timeout);
            }

            if ($r !== self::UDL_SUCCESS) {
                return $r;
            }

            $this->unserialize();
        } catch (Exception $e) {
            return self::UDL_FAILED;
        }

        return self::UDL_SUCCESS;
    }