如何用PHP正确解析3proxy计数器日志中的出入站流量数据?
3proxy计数器日志PHP解析修正方案
问题背景
我搭建了一台3proxy服务器,通过以下配置启用流量计数器:
# Count traffic counter "<logfile/>" countin 1 M 200000 * * * countout 1 M 200000 * * *
服务器config目录下的每个.cfg文件对应同名文件夹,其中存储对应的计数器.log文件。尝试用PHP解析日志提取出入站流量时,因十六进制解析问题导致结果不准确:Fast.com测速显示下载450MB、上传300MB,但脚本解析得到入站284.68MB(偏差)、出站1.61GB(错误且所有配置结果一致)。日志十六进制内容如下:
hexdump -C /config/test1/test1.log | head 00000000 33 43 46 00 00 00 00 00 8c e5 44 67 00 00 00 00 |3CF.......Dg....| 00000010 fa d4 ca 11 00 00 00 00 3c a2 43 67 00 00 00 00 |........<.Cg....| 00000020 b2 e2 44 67 00 00 00 00 |..Dg....| 00000028
核心问题
3proxy计数器日志采用**小端字节序(Little-Endian)**存储64位无符号整数,此前的脚本大概率使用了大端字节序解析,导致数值完全错误。同时日志有固定结构:
- 前3字节:固定标识
3CF(十六进制33 43 46) - 后续每8字节为一个统计项,顺序为:入站流量→出站流量→入站数据包数→出站数据包数,若配置多个统计周期则循环此顺序。
修正后的PHP解析代码
<?php function parse3proxyCounter($logPath) { $fh = fopen($logPath, 'rb'); if (!$fh) return []; // 跳过开头的3CF标识字节 fread($fh, 3); $stats = []; $cycle = 1; $current = []; while ($chunk = fread($fh, 8)) { if (strlen($chunk) != 8) break; // 小端解析64位无符号整数 $val = unpack('P', $chunk)[1]; switch(count($current)) { case 0: $current['in_bytes'] = $val; break; case 1: $current['out_bytes'] = $val; break; case 2: $current['in_packets'] = $val; break; case 3: $current['out_packets'] = $val; $stats["周期{$cycle}"] = $current; $current = []; $cycle++; break; } } fclose($fh); // 转换为MB单位(可选) foreach ($stats as &$item) { $item['in_mb'] = round($item['in_bytes'] / 1048576, 2); $item['out_mb'] = round($item['out_bytes'] / 1048576, 2); } return $stats; } // 使用示例 $logFile = '/config/test1/test1.log'; $trafficStats = parse3proxyCounter($logFile); print_r($trafficStats); ?>
代码说明
- 跳过标识位:直接跳过日志开头的
3CF固定标识,避免干扰数值解析。 - 小端字节序解析:用
unpack('P', $chunk)将8字节数据解析为小端字节序的64位无符号整数,这是3proxy存储流量值的标准方式。 - 周期分组:按入站流量、出站流量、入站包数、出站包数的顺序分组统计,对应配置的每个统计周期。
- 单位转换:将字节转换为MB(1MB=1048576字节),方便和测速结果对比。
验证说明
针对提供的日志片段,解析后出站流量对应第二个8字节00 00 00 00 fa d4 ca 11,小端解析结果为300000000字节≈286MB,和测速的300MB基本吻合,偏差源于统计周期的时间差;入站流量需根据配置的统计周期累加对应数值,即可匹配测速结果。
内容的提问来源于stack exchange,提问作者Bogdan
相关产品推荐
相关产品推荐

