读取无线电流时关闭 InputStream 挂起

问题描述

我正在使用 Apache HttpClient 从无线电流中获取元数据。我想要做的是发出 GET 请求,读取一些字节然后关闭流。对于某些流,它可以正常工作,但对于其他一些流,流的关闭会挂起。看起来它仍在接收数据并且在它发生时没有关闭连接。

    public SongInfo retrieveMetadata(String streamUrl) {
        HttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(streamUrl);
        httpGet.addHeader("Icy-MetaData","1");
        httpGet.addHeader("Connection","close");
        httpGet.addHeader("Accept","");
        HttpResponse response;
        try {
            response = httpClient.execute(httpGet);
        } catch (IOException e) {
            log.warn("An exception occurred while fetching the stream",e);
            return null;
        }

        if (response.getStatusLine().getStatusCode() != 200) {
            log.warn("Could not fetch stream. Status line: "+ response.getStatusLine());
            return null;
        }

        int metadataOffset = retrieveMetadataOffset(response);

        if (metadataOffset == 0) {
            log.info("Could not find metadata for url:"+ streamUrl);
            return null;
        }

        List<Metadata> metadata = extractMetadata(response,metadataOffset);
        if (metadata == null || metadata.isEmpty()) {
            return null;
        }
        return extractSongInfo(metadata);
    }
    private List<Metadata> extractMetadata(HttpResponse response,int metadataOffset) {
        String metadataStr;
        try(InputStream stream = response.getEntity().getContent()) {
            if (stream.skip(metadataOffset) != metadataOffset) {
                log.warn("Something went wrong while skipping to metadata offset");
                return null;
            }
            int metaDataLength = stream.read() * 16;
            metadataStr = getMetadataStr(stream,metaDataLength);
            if (metadataStr == null) {
                return null;
            }
        } catch (IOException e) {
            log.warn("Something went wrong while reading the stream",e);
            return null;
        } //Hangs here
        //rest of the method
    }

我注意到 response.getEntity().getContent() 返回的流是 EofSensorInputStream 类型,所以我想知道它是否正在等待一个永远不会到来的 EOF 字符。

代码正常运行且流正常关闭的流示例:https://icecast.omroep.nl/radio2-bb-mp3http://live-mp3-128.kexp.org 代码无法正常工作的流示例,因为流永远不会关闭并永远挂起:https://kexp-mp3-128.streamguys1.com/kexp128.mp3

解决方法

出现这个问题是因为在内容流上调用 close() 会尝试消耗剩余的内容。这目前在 documentation 中没有明确提及(另见 HTTPCORE-656),但在 tutorials 中提及:

关闭内容流和关闭响应的区别在于,前者会尝试通过消耗实体内容来保持底层连接处于活动状态,而后者会立即关闭并丢弃连接。
[...]
然而,在某些情况下,只需要检索整个响应内容的一小部分,而消耗剩余内容和使连接可重用的性能损失太高,在这种情况下,可以通过关闭来终止内容流回应。

因此,在您的情况下,关闭 InputStream 返回的 getContent() 而仅关闭 HttpResponse(您显然还没有这样做)似乎是合适的).

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...