NanoHTTPD - 写入套接字而不是复制字符串来传递页面

如何解决NanoHTTPD - 写入套接字而不是复制字符串来传递页面

这个问题不是关于 NanoHTTPD 如何提供流媒体内容,或者它如何在提供页面后保持 HTTP 套接字连接打开。

我非常负责任地使用 HTML.java 生成 HTML,方法是传入一个将所有内容组合成字符串的 Writer。

然后我的代码复制该字符串并将其放入 newFixedLengthResponse() 中,后者将 HTML 发送给客户端。

这意味着,在我的 HTML 生成器写入 Writer stringStream 的整个过程中,一个真正的流 - 网络浏览器的套接字 - 是打开的,什么也不做。虽然我的 stringStream 做了太多 - 缓冲越来越多的内存......

我不能自己找到那个套接字,然后把它放到我的 HTML 生成器中吗?这样,当我计算 html.div() 时,“

我知道大多数网络服务器不会这样做,而且它们都在内存中缓冲巨大的字符串,而不是有效地将它们流式传输出来......

对于我的下一个魔术,我将使 HTTPS 工作 C-;

1 个答案:

答案 0 :(得分:0)

即使在虚拟内存和 TB RAM 的时代,流也比字符串更高效。当我最初发布这个问题时,我不小心没有注意到 HTTPSession 对象已经有一个 outputStream 成员。所以第一步是升级它。将此添加到 IHTTPSession:

OutputStream getoutputStream();

现在将其添加到 HTTPSession:

public OutputStream getoutputStream() {
    return outputStream;
}

并将此方法添加到响应中:

public final void sender(@NonNull OutputStream outputStream,@NonNull Runnable run) {
    SimpleDateFormat gmtFrmt = new SimpleDateFormat("E,d MMM yyyy HH:mm:ss 'GMT'",Locale.US);
    gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
        if (status == null) {
            throw new Error("sendResponse(): Status can't be null.");
        }
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream,new ContentType(mimeType).getEncoding())),false);
        pw.append("HTTP/1.1 ").append(status.getDescription()).append(" \r\n");
        if (mimeType != null) {
            printHeader(pw,"Content-Type",mimeType);
        }
        if (getHeader("date") == null) {
            printHeader(pw,"Date",gmtFrmt.format(new Date()));
        }
        for (Entry<String,String> entry : header.entrySet()) {
            printHeader(pw,entry.getKey(),entry.getValue());
        }
        for (String cookieHeader : cookieHeaders) {
            printHeader(pw,"Set-Cookie",cookieHeader);
        }
        if (getHeader("connection") == null) {
            printHeader(pw,"Connection",(keepAlive ? "keep-alive" : "close"));
        }
        long pending = data != null ? contentLength : 0;
        if (requestMethod != Method.HEAD && chunkedTransfer) {
            printHeader(pw,"transfer-encoding","chunked");
        }
        pw.append("\r\n");
        pw.flush();

        run.run(); // <-- your streaming happens here

        outputStream.flush();
        NanoHTTPD.safeClose(data);
    } catch (IOException ioe) {
        NanoHTTPD.LOG.log(Level.SEVERE,"Could not send response to the client",ioe);
    }
}

请注意,我们可以剔除更多未使用的东西。例如,浏览器不值得知道 Content-Length;它只需要拉动页面,看看它会得到什么。

现在应用覆盖 .serve() 并使其看起来像这样:

    public final Response serve(IHTTPSession session) {
        OutputStream outputStream = session.getoutputStream();

        newFixedLengthResponse("").sender(outputStream,() -> {
            new OutputStreamWriter(outputStream).write("Yo,World!");
        } ;

        return null;
    }

最后,为了防止 NanoHTTPD 对 null 进行 snit-fit,进入 HTTPSession 并用返回替换这个 throw

//            throw new ResponseException(Status.INTERNAL_ERROR,"SERVER INTERNAL ERROR: Serve() returned a null response.");
            return;

进一步清理显然是可能的,但基本原则仍然是,因为我的应用程序使用流(特别是 com.googlecode.jatl.HTML)来构建它的页面,网络浏览器可以绘制我们页面的顶部 我们仍在生成页面底部

解决方法

即使在虚拟内存和 TB RAM 的时代,流也比字符串更高效。当我最初发布这个问题时,我不小心没有注意到 HTTPSession 对象已经有一个 outputStream 成员。所以第一步是升级它。将此添加到 IHTTPSession:

OutputStream getOutputStream();

现在将其添加到 HTTPSession:

public OutputStream getOutputStream() {
    return outputStream;
}

并将此方法添加到响应中:

public final void sender(@NonNull OutputStream outputStream,@NonNull Runnable run) {
    SimpleDateFormat gmtFrmt = new SimpleDateFormat("E,d MMM yyyy HH:mm:ss 'GMT'",Locale.US);
    gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));

    try {
        if (status == null) {
            throw new Error("sendResponse(): Status can't be null.");
        }
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream,new ContentType(mimeType).getEncoding())),false);
        pw.append("HTTP/1.1 ").append(status.getDescription()).append(" \r\n");
        if (mimeType != null) {
            printHeader(pw,"Content-Type",mimeType);
        }
        if (getHeader("date") == null) {
            printHeader(pw,"Date",gmtFrmt.format(new Date()));
        }
        for (Entry<String,String> entry : header.entrySet()) {
            printHeader(pw,entry.getKey(),entry.getValue());
        }
        for (String cookieHeader : cookieHeaders) {
            printHeader(pw,"Set-Cookie",cookieHeader);
        }
        if (getHeader("connection") == null) {
            printHeader(pw,"Connection",(keepAlive ? "keep-alive" : "close"));
        }
        long pending = data != null ? contentLength : 0;
        if (requestMethod != Method.HEAD && chunkedTransfer) {
            printHeader(pw,"Transfer-Encoding","chunked");
        }
        pw.append("\r\n");
        pw.flush();

        run.run(); // <-- your streaming happens here

        outputStream.flush();
        NanoHTTPD.safeClose(data);
    } catch (IOException ioe) {
        NanoHTTPD.LOG.log(Level.SEVERE,"Could not send response to the client",ioe);
    }
}

请注意,我们可以剔除更多未使用的东西。例如,浏览器不值得知道 Content-Length;它只需要拉动页面,看看它会得到什么。

现在应用覆盖 .serve() 并使其看起来像这样:

    public final Response serve(IHTTPSession session) {
        OutputStream outputStream = session.getOutputStream();

        newFixedLengthResponse("").sender(outputStream,() -> {
            new OutputStreamWriter(outputStream).write("Yo,World!");
        } ;

        return null;
    }

最后,为了防止 NanoHTTPD 对 null 进行 snit-fit,进入 HTTPSession 并用返回替换这个 throw

//            throw new ResponseException(Status.INTERNAL_ERROR,"SERVER INTERNAL ERROR: Serve() returned a null response.");
            return;

进一步清理显然是可能的,但基本原则仍然是,因为我的应用程序使用流(特别是 com.googlecode.jatl.HTML)来构建它的页面,网络浏览器可以绘制我们页面的顶部 我们仍在生成页面的底部。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?