PHP 网络编程基础 stream 函数

相关函数

  • 服务端函数
stream_socket_server( string $local_socket [, int &$errno [, string &$errstr [, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN [, resource $context ]]]] ) : resource

$flags参数,如果是用udp通信的话,STREAM_SERVER_LISTEN 是不需要的,$context则是上下文,后面有单独的函数来生成此类型,还有需要注意的是,stream_socket_server和socket_create的返回值虽然都是resource,但两个不能通用,socket扩展中有单独的函数来转换这两者。

stream_socket_accept ( resource $server_socket [, float $timeout = ini_get("default_socket_timeout") [, string &$peername ]] )  : resource

接收客户端的连接(udp通信时,不需要用到此函数)
类似socket_accept,需要注意的是第一个参数,只能是stream_socket_server的返回值

  • 客户端函数
stream_socket_client ( string $remote_socket [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") [, int $flags = STREAM_CLIENT_CONNECT [, resource $context ]]]]] )  : resource

创建socket客户端,连接到服务端

  • 读写函数

读取数据

fread ( resource $handle , int $length ) : string

写入数据

fwrite ( resource $handle , string $string [, int $length ] ) : int

上面两个函数和我们平常读取文件一样,正如我们说的,操作文件也是操作一种流,所以方法通用

接收数据,最后参数是引用,用于获取远端链接的地址

stream_socket_recvfrom ( resource $socket , int $length [, int $flags = 0 [, string &$address ]] ) : string

发送数据

stream_socket_sendto ( resource $socket , string $data [, int $flags = 0 [, string $address ]] ) : int

上面两个函数和 fread 和 fwrite 基本一致,但功能更多一些,$flags 参数可以设置发送OOB数据,而 $address 参数则多是用于udp通信,由于 udp 通信时不使用 stream_socket_accept,所以无法获取到新的resource,那就无法向指定的客户端中写数据,所以一般先用 stream_socket_recvfrom 获取最后引用参数 $address 即为客户端的地址,然后再用stream_socket_sendto设置$address,来向指定的客户端发送。

利用stream函数做一个日志记录,以及收集日志数据的公共类

<?php

namespace app\common\service;

use think\Config;

class Logger extends Base
{
    const IDENTIFICATION = '[XXXX]'; //日志标识
    // target,支持file,udp模式
    const TargetFile = 'file';
    const TargetUdp = 'udp';
    // level
    const LevelInfo = '[INFO]';
    const LevelError = '[ERROR]';
    // type
    const TypeMySQL = '[MYSQL]';
    const TypeCurl = '[CURL]';
    // path
    const PathBase = '/data/logs/';

    public static function error($data)
    {
        return self::log($data, self::LevelError);
    }

    public static function log($data, $level = self::LevelInfo)
    {
        $conf = self::getConf();
        $target = empty($conf['target']) ? self::TargetFile : $conf['target'];
        switch ($target) {
            case self::TargetUdp:
                self::logUdp($data, $level);
                break;
            case self::TargetFile:
            default:
                self::logFile($data, $level);
                break;
        }
    }

    private static function logUdp($data, $level)
    {
        $conf = self::getConf();
        $handle = stream_socket_client("udp://{$conf['host']}:{$conf['port']}", $errno, $errstr);
        if (!$handle) {
            return false;
        }

        fwrite($handle, self::formatData($data, $level));

        return fclose($handle);
    }

    private static function logFile($data, $level)
    {
        return file_put_contents(self::getFile(), self::formatData($data, $level) . PHP_EOL, FILE_APPEND);
    }

    /**
     * @param $data
     * @param $level
     * @return string 例子解释:402b618f3d[2020-09-11 15:45:53] [INFO] {"type":"[MYSQL]","time":"0.003811","connect":"db connect use time"}
     *      402b618f3d 同一个HTTP或者CLI执行ID
     *      [2021-01-11 15:45:53] 时间
     *      [INFO] 错误级别 @see LevelError|LevelInfo
     *      {"type":"[MYSQL]" 日志类型
     */
    private static function formatData($data, $level)
    {
        $data = is_string($data) ? $data : json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $str = self::IDENTIFICATION;
        if (!self::isCli()) {
            $str .= ' requestID:' . REQUESTID;
        }
        return $str . ' [' . date('Y-m-d H:i:s') . "] $level " . $data;
    }

    private static function isCli()
    {	//define ('PHP_SAPI', "cli");
        return PHP_SAPI == 'cli';
    }

    public static function run()
    {
        $conf = self::getConf();
        $server = "udp://{$conf['host']}:{$conf['port']}";
        $socket = stream_socket_server($server, $errno, $errstr, STREAM_SERVER_BIND);
        if (!$socket) {
            self::setError("$errstr ($errno)");
            return false;
        }

        while (true) {
            $revMsg = stream_socket_recvfrom($socket, 4096, 0, $peer);
            file_put_contents(self::getFile(), $revMsg . PHP_EOL, FILE_APPEND);
        }

        return true;
    }

    public static function getFile($path = null)
    {
        if (null === $path) {
            $path = self::getConf()['logFile'] ?: self::PathBase;
        }

        $fileArr = explode('/', $path);
        array_pop($fileArr);
        $dir = implode('/', $fileArr);
        if (!is_dir($dir)) {
            mkdir($dir, 0777, true);
            chown($dir, 'www');
        }

        $logFile = $dir . '/' . (Func::isDebug() ? date('md') : date('md-H')) . '.log';
        if (!is_file($logFile)) {
            touch($logFile);
            chmod($logFile, 0777);
        }

        return $logFile;
    }

    private static function getConf()
    {
        return Config::get('logger');
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值