如何使用Indy TIdHTTP正确读取网络电台的无限流?
问题根源
网络电台的流是无限持续的,TIdHTTP.Get()会一直读取直到服务器主动关闭连接,所以你的程序会卡在Get()调用上,永远不会执行后续代码。
解决方案:分段读取并检测XML
你需要手动控制读取过程,每次读取一小段数据,同时检查是否出现XML的特征(比如<开头的标记),一旦检测到目标XML或者读取到足够数据,就停止读取。
修改后的代码示例:
procedure TformMain.GetUrlStream(url: string; var m: TMemoryStream); var IdHTTP1: TIdHTTP; IO: TIdSSLIOHandlerSocketOpenSSL; Buffer: array[0..4095] of Byte; BytesRead: Integer; TempStr: string; FoundXML: Boolean; begin IdHTTP1 := TIdHTTP.Create(nil); try IO := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1); IO.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdHttp1.IOHandler := IO; IdHttp1.ConnectTimeout := 5000; IdHttp1.ReadTimeout := 10000; // 设置读取超时,防止无数据时卡住 IdHttp1.Request.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'; IdHttp1.Request.Connection := 'keep-alive'; // 无限流不需要close,保持连接 // 手动发送GET请求,避免自动读取全部流 IdHTTP1.Request.Method := 'GET'; IdHTTP1.Connect(url, IdHTTP1.DefaultPort); IdHTTP1.SendRequest(IdHTTP1.Request.Method, url); IdHTTP1.ReadResponseHeaders; FoundXML := False; m.Clear; while not FoundXML do begin // 每次读取4KB数据块 BytesRead := IdHTTP1.IOHandler.Read(Buffer, SizeOf(Buffer)); if BytesRead <= 0 then Break; // 将读取到的数据写入内存流 m.WriteBuffer(Buffer, BytesRead); // 检测XML特征(可根据实际元数据格式调整,比如找特定根节点) SetString(TempStr, PChar(@Buffer[0]), BytesRead); if (Pos('<', TempStr) > 0) and (Pos('>', TempStr) > 0) then begin FoundXML := True; // 可选:多读一次确保XML片段完整 BytesRead := IdHTTP1.IOHandler.Read(Buffer, SizeOf(Buffer)); if BytesRead > 0 then m.WriteBuffer(Buffer, BytesRead); end; end; finally IdHTTP1.Disconnect; IdHTTP1.Free; end; end;
关键说明
- 放弃
Get()的自动读取逻辑,改用手动连接、发送请求、读取响应头的方式,完全控制读取过程。 - 设置
ReadTimeout,如果超过指定时间未读取到数据,会抛出异常,可捕获后终止读取。 - 分段读取数据并实时检测XML特征,找到目标后立即停止,避免无限等待。
- 你需要根据实际的元数据格式调整检测逻辑,比如已知元数据根节点是
<metadata>,可以直接检测该字符串提高准确性。
内容的提问来源于stack exchange,提问作者ratmalwer
相关产品推荐
相关产品推荐

