Java Tomcat-Servlet响应阻塞-刷新问题

Java Tomcat-Servlet响应阻塞-刷新问题,java,tomcat,response,blocking,flush,Java,Tomcat,Response,Blocking,Flush,我正在使用Tomcat 6.0.36和JRE 1.5.0,我正在Windows 7上进行开发工作 作为我正在做的一些工作的概念证明,从Java代码中,我将HTTP通过套接字向servlet发布一些XML。这个 servlet然后回显xml。在我的第一个实现中,我将两端的输入流传递给XML 文档工厂来提取通过网络发送的xml。这在servlet中工作顺利,但失败了 在客户端。结果表明,它在客户端失败,因为读取响应被阻塞 直到文档工厂超时并在整个响应到达之前抛出异常。 (文档工厂的行为现在是没有意义

我正在使用Tomcat 6.0.36和JRE 1.5.0,我正在Windows 7上进行开发工作

作为我正在做的一些工作的概念证明,从Java代码中,我将HTTP通过套接字向servlet发布一些XML。这个 servlet然后回显xml。在我的第一个实现中,我将两端的输入流传递给XML 文档工厂来提取通过网络发送的xml。这在servlet中工作顺利,但失败了 在客户端。结果表明,它在客户端失败,因为读取响应被阻塞 直到文档工厂超时并在整个响应到达之前抛出异常。 (文档工厂的行为现在是没有意义的,因为正如我在下面所描述的,我遇到了同样的阻塞问题 不使用文档工厂。)

为了解决这个阻塞问题,我随后提出了一个更简单的客户端代码版本和 servlet。在这个更简单的版本中,我从等式中删除了文档生成器。现在两边都有密码了 只需从各自的输入流中读取文本

不幸的是,我的响应仍然存在这个阻塞问题,正如我在下面描述的,它还没有被解决 只需调用response.flushBuffer()。谷歌搜索只检索到一个我能找到的相关主题 ()但这不完全一样 问题

我已经包括了我的代码,并在下面解释了确切的问题

这是我的servlet代码(请记住,这是简单的概念验证代码,而不是生产代码)

这是我的客户端代码

import java.io.BufferedOutputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.Socket;

public final class SimpleSender {

    private String host;
    private String path;
    private int port;

    public SimpleSender(String host, String path, int port) {

        this.host = host;
        this.path = path;
        this.port = port;

    }

    public void execute() {

        Socket connection = null;
        String line;

        try {
            byte[] xmlBytes = getXmlBytes();
            byte[] headerBytes = getHeaderBytes(xmlBytes.length);

            connection = new Socket(this.host, this.port);

            OutputStream outputStream = new BufferedOutputStream(connection.getOutputStream());
            outputStream.write(headerBytes);
            outputStream.write(xmlBytes);
            outputStream.flush();

            LineNumberReader lineReader
                = new LineNumberReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));

            while((line = lineReader.readLine()) != null) {
                System.out.println("line: " + line);
            }

            System.out.println("The response is read.");
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            try {
                connection.close();
            }
            catch(Exception e) {}
        }
    }

    private byte[] getXmlBytes() throws Exception {

        StringBuffer sb = null;

        sb = new StringBuffer()
            .append("<my-xml>\n")
            .append("Hello to myself.\n")
            .append("</my-xml>\n");

        return sb.toString().getBytes("UTF-8");
    }

    private byte[] getHeaderBytes(int contentLength) throws Exception {

        StringBuffer sb = null;

        sb = new StringBuffer()
            .append("POST ")
            .append(this.path)
            .append(" HTTP/1.1\r\n")
            .append("Host: ")
            .append(this.host)
            .append("\r\n")
            .append("Content-Type: text/xml;charset=UTF-8\r\n")
            .append("Content-Length: ")
            .append(contentLength)
            .append("\r\n")
            .append("\r\n");

        return sb.toString().getBytes("UTF-8");
    }

}
注意,当阻塞发生在客户端时,服务器端的整个输出是完全可见的

sb.append("An additional line to see when it turns up on the client.\n");
从那以后,我尝试在服务器端刷新,试图解决这个问题。我独立尝试了两种冲洗方法: response.flushBuffer()和response.getOutputStream().flush()。用这两种冲洗方法,我仍然有阻塞 客户端(但在响应的不同部分),但我还有其他问题。客户在这里 封锁,

客户端的此输出有三个问题。首先,响应的读取仍然是阻塞的,这是错误的 只是在响应的不同部分后阻塞。其次,我返回了未预料到的字符(“4a”、“0”)。 最后,标题已经更改。我丢失了内容长度标题,并获得了 “传输编码:分块”标题

因此,如果没有刷新,我的响应在发送最后一行和终止响应之前被阻塞。然而, 在刷新时,响应仍然被阻塞,但现在我得到了我不想要的字符,并更改了我需要的标题 我不想要

在Tomcat中,我的连接器具有默认定义

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
上述代码块位于

HELLO FROM MAIN
然后20秒后,完成wtih

The response is read: 235
GOODBYE FROM MAIN
我认为上面的结论表明,问题不在于在客户端使用行读取器

sb.append("An additional line to see when it turns up on the client.\n");
在上面的行中添加返回只是将块延迟到后面的一行。我在手术前就已经测试过了,我刚刚又测试了一次

If you want to do your own HTTP parser, you have to read through the headers until you get two blank lines.
是的,我确实知道,但在这个精心设计的简单例子中,这是一个没有实际意义的观点。在客户机上,我只是简单地输出返回的HTTP消息、头和所有内容

Then you need to scan the headers to see if you had a Content-Length header. If there is no Content-Length then you are done. If there is a Content-Length you need to parse it for the length, then read exactly that number of additional bytes from the stream. This allows HTTP to transport both text data and also binary data which has no line feeds.
是的,所有这些都是真的,但在这个精心设计的简单例子中并不相关

I recommend you replace the guts of your client code HTTP writer/parse with a pre-written client library that handles these details for you.
我完全同意。实际上,我希望将流的处理传递给xml文档工厂。作为处理阻塞问题的一种方法,我还研究了ApacheCommonsHttpClient。新版本(httpcomponents)仍然让开发人员来处理post返回流(据我所知),所以这没有用。如果你能推荐另一个图书馆,我肯定会感兴趣的

我不同意你的观点,但我感谢你的回答,我的意思是没有冒犯或任何负面暗示我的不同意。很明显,我做错了什么,或者没有做我应该做的事情,但我不认为读线器是问题所在。另外,如果我脸红,那些时髦的角色从哪里来?为什么在客户端没有使用行读取器时会发生阻塞


此外,我还复制了Jetty上的问题。因此,这绝对不是Tomcat的问题,而是“我”的问题。我做错了什么,但我不知道是什么。

您的服务器代码看起来不错。问题在于您的客户端代码。它不遵守HTTP协议,并且将响应视为一堆行

服务器上的快速修复。改为:

    sb.append("An additional line to see when it turns up on the client.\n");
您的客户端正在阻塞,因为您正在使用readLine读取邮件正文。readLine挂起,因为正文未以换行结束。最后,Tomcat超时,关闭连接,缓冲读取器检测到这一点并返回剩余数据

如果您在上面(对服务器)进行了更改,这将使您的客户端看起来像您期望的那样工作。尽管这仍然是错误的

如果你想做你自己的HTTP解析器,你必须通读标题,直到你得到两个空行。然后,您需要扫描标题以查看是否有内容长度的标题。如果没有内容长度,那么就完成了。如果有一个内容长度,您需要对其进行长度解析,然后从流中准确读取该数量的额外字节。这允许HTTP传输文本数据和没有换行符的二进制数据


我建议您用一个预编写的客户端库来替换客户端代码HTTP writer/parse的核心,该库可以为您处理这些细节。

LOL好的,我做错了什么(由于省略)。我的问题的解决方案是什么?将以下标头添加到我的http请求中

Connection: close
那很简单。如果没有这一点,这种联系就不会消失。我的代码依赖于服务器,这意味着它已经完成,但服务器仍在侦听
sb.append("An additional line to see when it turns up on the client.\n");
If you want to do your own HTTP parser, you have to read through the headers until you get two blank lines.
Then you need to scan the headers to see if you had a Content-Length header. If there is no Content-Length then you are done. If there is a Content-Length you need to parse it for the length, then read exactly that number of additional bytes from the stream. This allows HTTP to transport both text data and also binary data which has no line feeds.
I recommend you replace the guts of your client code HTTP writer/parse with a pre-written client library that handles these details for you.
    sb.append("An additional line to see when it turns up on the client.\n");
Connection: close