HTTP分块传输

时间:2013-02-27 14:50:53

标签: java http tomcat chunked-encoding

我正在编写一个使用分块传输编码连接到服务的客户端应用程序。该服务偶尔断开连接,我被告知这是因为我们在请求中发送了一个零块,因此Tomcat关闭了连接。

我正在使用Java HttpUrlConnection类来建立连接,我不知道它为什么会发送零块以及如何防止它这样做。

这是代码。

URL m5url = new URL("https://hostedconnect.m5net.com/bobl/bobl?name=org.m5.apps.v1.cti.ClickToDial.subscribe");
StringBuffer sb = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"standalone=\"yes\"?>" 
                                   + "<Command>" 
                                   + "<Name>org.m5.apps.v1.cti.ClickToDial.subscribe</Name>"
                                   + "<Id>1</Id>" 
                                   + "<User>" + m5username + "</User>" 
                                   + "<Password>" + m5password + "</Password>" 
                                   + "<FormattedXml>true</FormattedXml>" 
                                   + "<ShallowResponse>FULL</ShallowResponse>" 
                                   + "</Command>");

conn = (HttpURLConnection) m5url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setReadTimeout(SESSION_TIMEOUT);
conn.setChunkedStreamingMode(0);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);

out = new DataOutputStream(conn.getOutputStream());
conn.connect();
out.writeBytes(sb.toString());
out.flush();

当我inputstream.readline nullpublic class M5Connection { public static final String urlBase = "/bobl/bobl"; public static final String ENCODING = "ISO-8859-1"; public static final String DELIMITER = "\r\n"; protected URL url; private InputStream inputStream; protected OutputStream outputStream; protected Socket socket; protected BufferedReader reader; private boolean bProcessedHeaders; protected String resp = null; protected String errorMessage = null; /** * Start a new connection to the BOBL server. * @param server server name:port to connect to * @throws IOException */ protected void initConnection(String server, int timeout) throws IOException { url = new URL(server + urlBase); int port = url.getPort(); if (server.startsWith("https")) { if (port == -1) port = 443; else if (port == 80 || port == -1)port = 8080; } if (server.startsWith("https") == false) { socket = new Socket(url.getHost(), port); } else { SocketFactory socketFactory = SSLSocketFactory.getDefault(); socket = socketFactory.createSocket(url.getHost(), port); } socket.setSoTimeout(timeout); socket.setKeepAlive(true); socket.setSoLinger(false, 0); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); reader = new BufferedReader(new InputStreamReader(inputStream)); } public void initHttpsConnection(String server, int timeout) throws IOException { initConnection(server,timeout); sendHeaders(); bProcessedHeaders = false; } private void sendHeaders() throws IOException { String path = url.getPath(); StringBuffer outputBuffer = new StringBuffer(); outputBuffer.append("POST " + path + " HTTP/1.1" + DELIMITER); outputBuffer.append("Host: " + url.getHost() + DELIMITER); outputBuffer.append("User-Agent: CometTest" + DELIMITER); outputBuffer.append("Connection: keep-alive" + DELIMITER); outputBuffer.append("Content-Type: text/plain" + DELIMITER); outputBuffer.append("Transfer-Encoding: chunked" + DELIMITER); outputBuffer.append(DELIMITER); byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); } /** Send some data to the server, HTTP/1.1 chunked style. */ public void send(String chunkData) throws IOException { byte[] chunkBytes = chunkData.getBytes(ENCODING); String hexChunkLength = Integer.toHexString(chunkBytes.length); StringBuffer outputBuffer = new StringBuffer(); outputBuffer.append(hexChunkLength); outputBuffer.append(DELIMITER); outputBuffer.append(chunkData); outputBuffer.append(DELIMITER); byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); outputBuffer = new StringBuffer(); outputBuffer.append("0"); outputBuffer.append(DELIMITER); outputBuffer.append(DELIMITER); outputBytes = outputBuffer.toString().getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); } /** * Wait for a response from the server. * @return the string that the server returned. * @throws IOException */ public String getRawResponse() throws IOException { String s; // just after we connect we expect to see the HTTP headers. Read and discard if (!bProcessedHeaders) { while (true){ String line = reader.readLine(); System.out.println("HEADER: " + line); if (line == null || line.equals("\r\n") || line.equals("")) break; } bProcessedHeaders = true; } while (true) { s = getChunk(); if (s == null) return null; if (s.equals("")) { continue; } // server will not emit XML if it is having real troubles if (s.charAt(0) != '<' || s.startsWith("<html>")) { System.out.println("Server says: " + s); continue; } return s; } } /** * Expect chunked excoding back from the server. Read and return a chunk. * @return a string containing the HTTP chunk * @throws IOException */ private String getChunk() throws IOException { StringBuffer buf = new StringBuffer(); while (true) { // HTTP chunked mode, expect to see a line with the length in hex of the chunk that follows String s = reader.readLine(); if (s == null) throw new IOException(); if (s.length() == 0) continue; int toread; try { toread = Integer.parseInt(s, 16); } catch (NumberFormatException e) { System.out.println("Number format error: " + s); return ""; } if (toread == 0) { return null; } // read the chunk char[] data = new char[toread]; int read = 0; while (read != toread) { read += reader.read(data, read, toread - read); } buf.append(data, 0, read); // for some reason tomcat only sends data in up to 8192 byte chunks if (toread != 8192) break; } return buf.toString(); } public void close() { try { socket.close(); } catch (IOException e) {} } public static void main(String[] args) throws Exception { M5Connection cnx = new M5Connection(); cnx.initHttpsConnection("https://hostedconnect.m5net.com/bobl/bobl?name=org.m5.apps.v1.cti.ClickToDial.subscribe", 0); Thread.sleep(100); // // Create and send an XML command to listen for call state changes on our TN // String format = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + "<Command>" + " <Name>org.m5.apps.v1.cti.ClickToDial.subscribe</Name>" + " <Id>1</Id>" + " <User></User>" + " <Password></Password>" + " <FormattedXml>true</FormattedXml>" + " <ShallowResponse>FULL</ShallowResponse>" + "</Command>"; String command = format; System.out.println("SENDING " + command + "\n ------------ "); cnx.send(command); // // Now just wait for the responses // while (true) { String resp = cnx.getRawResponse(); System.out.println(resp); } } } ,但它有时会有效,有时则无效。

enter image description here 好的,所以我很困惑。我放弃了使用HttpURLConnection并开始使用Socket类并手动编写所有头文件和数据。没有发送零块,它似乎一直在工作。除了在调试器中运行它之外,零块似乎一直在工作,它在上面得到了同样的错误。因此,在发送标头之后,在发送数据并在没有调试器的情况下运行数据之前,我放置了一个睡眠(100),并且始终出现错误。所以我假设在HttpURLConnection类中发送标题后有一个延迟,这就是为什么它有时有效,而不是其他时间。我可以不发送零块,但我真的想知道为什么会导致错误。有任何想法吗?我在想Tomcat中有一个错误。

这是代码。

{{1}}

1 个答案:

答案 0 :(得分:3)

  

我被告知这是因为我们在请求中发送了一个零块

你被误导了。零的最后一块是正确的,表明转移结束。见RFC 2616 #3.6.1。发送一个没什么问题,Tomcat不应该(通常也不会)通过关闭连接做出反应。