2013-02-27 53 views
0

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

我使用JavaHttpUrlConnection类来进行连接,我不知道为什么它会发送一个零块,以及如何防止它做到这一点。

这是代码。

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这是null,但有时工作,有时没有。

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

这是代码。

public 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); 
     } 
    } 
} 
+0

您是否尝试过在setChunkedStreamingMode()中使用另一个数字?如果我正确理解您的问题,则可以根据http://docs.oracle.com/javase/7/docs/api/index.html控制块大小。 – Luis 2013-02-27 15:15:47

+0

尝试在调用'getOutputStream'之前调用'connect'。我从来没有看到它按照您使用的顺序完成,我可以想象'连接'践踏现有的流。 – 2013-02-27 15:27:44

+0

我试着改变setchunkedstreamingmode并移动getoutputstream之前的连接,它仍然出现问题。我附上了请求和响应的图像。 – user1309036 2013-02-27 15:50:04

回答

3

我被告知这是因为我们在

你被误导的请求发送一个零块。零的最后一块是正确的,表示传输结束。见RFC 2616 #3.6.1。发送一个没有问题,Tomcat不应该(并且几乎肯定不会)通过关闭连接来做出反应。

相关问题