如何排查Server-Sent Events获取Icecast流JSON元数据失败问题
问题:Icecast流元数据获取公网部署异常
本地开发时,通过HTML、JS、PHP配合获取Icecast流元数据的功能完全正常,但部署到公网服务器后出现以下问题:
- 初始遇到CORS错误,添加访问控制头后解决,但PHP无法解析Icecast的
status-json.xsl数据 - 已将本地正常工作的
file_get_contents替换为curl,但仍无法获取有效数据 - Icecast子域名位于Apache反向代理后,流可正常播放,且
status-json.xsl可通过公网浏览器正常访问 - Firefox控制台显示PHP接口返回200状态,但输出内容中本该显示元数据的位置只有句号,PHP文件权限为755
HTML代码
<div id="scroll-container"> <span class="scroll-text" id="scroll-text"><span> </div>
JavaScript代码
//Get the stream meta from xml messages = new Array(); if(typeof(EventSource) !== "undefined") { var source = new EventSource("https://www.example.com/radio/js/json.php"); source.onmessage = function(event) { if (messages.length == 2) messages.splice(-1, 1); messages.splice(0, 0, event.data); document.getElementById("scroll-text").innerHTML = messages.join("<br />"); }; } else { document.getElementById("scroll-text").innerHTML = "Sorry, your browser does not support server-sent events..."; }
PHP代码
header('Access-Control-Allow-Origin: *'); //header('Access-Control-Allow-Origin: https://www.example.com'); header('Content-Type: application/json'); header('Accept: application/json'); header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_URL, 'https://listen.abengnews.com/status-json.xsl'); $json = curl_exec($ch); curl_close($ch); //$json = file_get_contents('https://listen.example.com/status-json.xsl'); $obj = json_decode($json); $retry = 15000; echo "data:{$obj->icestats->source->server_name}. retry:{$retry} "; echo "data:{$obj->icestats->source->genre}. Now playing: retry:{$retry} "; echo "data:{$obj->icestats->source->title}. retry:{$retry} "; echo "data:{$obj->icestats->source->server_description} retry:{$retry} "; //$retry = 10000; flush();
浏览器直接访问status-json.xsl返回的JSON
{ "icestats": { "admin": "me@example.com", "host": "listen.example.com", "location": "Mississaauga, Ontario", "server_id": "Icecast 2.4.4", "server_start": "Sun, 21 May 2023 22:35:55 -0400", "server_start_iso8601": "2023-05-21T22:35:55-0400", "source": { "audio_info": "bitrate=128;samplerate=22050;channels=2", "bitrate": 128, "channels": 2, "genre": "News, Reggae", "listener_peak": 5, "listeners": 1, "listenurl": "http://listen.example.com/sounds.mp3", "samplerate": 22050, "server_description": "News and reggae", "server_name": "Example Radio", "server_type": "audio/mpeg", "server_url": "listen.example.com", "stream_start": "Mon, 22 May 2023 11:01:16 -0400", "stream_start_iso8601": "2023-05-22T11:01:16-0400", "title": "Burning Spear - Foggy Road", "dummy": null } } }
Firefox控制台响应
XHRGET https://www.example.com/radio/js/json.php [HTTP/1.1 200 OK 130295ms] 1 data:. 2 retry:15000 3 data:. Now playing: 4 retry:15000 5 data:. 6 retry:15000 7 data: 8 9 retry:15000 10 11
排查与修复方案
1. 定位瓶颈的步骤
- 检查curl请求错误:在PHP中添加curl错误捕获,查看服务器错误日志(如Apache的
error.log)确认问题$json = curl_exec($ch); if(curl_errno($ch)){ error_log('Curl error: ' . curl_error($ch)); } curl_close($ch); - 验证JSON解析结果:输出原始JSON内容和解析错误信息,确认是否格式异常
var_dump($json); echo json_last_error_msg(); - 测试服务器网络连通性:通过SSH在公网服务器执行
curl https://listen.abengnews.com/status-json.xsl,确认能否正常获取数据
2. 代码修正点
- 移除重复响应头:删除
Content-Type: application/json和Accept: application/json,保留text/event-stream(适配Server-Sent Events) - 添加空值保护:避免
$obj为空时触发致命错误 - 优化curl参数:添加超时设置,避免请求挂起;补充SSL验证相关参数(注意安全风险)
修正后的PHP代码:
header('Access-Control-Allow-Origin: *'); header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_URL, 'https://listen.abengnews.com/status-json.xsl'); $json = curl_exec($ch); if(curl_errno($ch)){ error_log('Curl error: ' . curl_error($ch)); echo "data:Failed to fetch stream data. retry:15000 "; curl_close($ch); flush(); exit; } curl_close($ch); $obj = json_decode($json); $retry = 15000; $server_name = isset($obj->icestats->source->server_name) ? $obj->icestats->source->server_name : 'Unknown'; $genre = isset($obj->icestats->source->genre) ? $obj->icestats->source->genre : 'Unknown'; $title = isset($obj->icestats->source->title) ? $obj->icestats->source->title : 'Unknown'; $server_desc = isset($obj->icestats->source->server_description) ? $obj->icestats->source->server_description : ''; echo "data:{$server_name}. retry:{$retry} "; echo "data:{$genre}. Now playing: retry:{$retry} "; echo "data:{$title}. retry:{$retry} "; echo "data:{$server_desc} retry:{$retry} "; flush();
内容的提问来源于stack exchange,提问作者Mark Lee
相关产品推荐
相关产品推荐

