我在寫一個客戶端應用程序,它使用分塊傳輸編碼連接到服務。該服務偶爾會斷開連接,我被告知這是因爲我們在請求中發送了零塊,因此Tomcat
會關閉連接。HTTP分塊傳輸
我使用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
這是null
,但有時工作,有時沒有。
好的,所以我很困惑。我放棄了使用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);
}
}
}
您是否嘗試過在setChunkedStreamingMode()中使用另一個數字?如果我正確理解您的問題,則可以根據http://docs.oracle.com/javase/7/docs/api/index.html控制塊大小。 – Luis 2013-02-27 15:15:47
嘗試在調用'getOutputStream'之前調用'connect'。我從來沒有看到它按照您使用的順序完成,我可以想象'連接'踐踏現有的流。 – 2013-02-27 15:27:44
我試着改變setchunkedstreamingmode並移動getoutputstream之前的連接,它仍然出現問題。我附上了請求和響應的圖像。 – user1309036 2013-02-27 15:50:04