背景
在工作中,我被问及一个问题CXF的WebClient是如何通过连接发送请求的,到最后引申为HttpURLConnection的TCP连接的重用问题。
问题描述
在一个线程中,多个方法调用HttpURLConnection conn = (HttpURLConnection) url.openConnection() 建立一次TCP连接还是多次TCP连接?
具体点讲,如下代码中HttpClient有3个方法,每个都调用 HttpURLConnection conn = (HttpURLConnection) url.openConnection() ,那么一个线程调用method1, method2 然后method3, JDK会为这个线程创建多少个TCP连接?
package art.programming.net;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* see
* http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html
*
*
* @author Alex Chen
*/
public class HttpClient {
public static void main(String... args){
try{
final URL url = new URL("http://www.google.cn");
HttpClient httpClient = new HttpClient();
httpClient.method1(url);
httpClient.method2(url);
httpClient.method3(url);
/**
new Thread(){
public void run(){
HttpClient httpClient = new HttpClient();
httpClient.method1(url);
httpClient.method2(url);
httpClient.method3(url);
}
}.start();
**/
}catch(Exception e){
}
}
public void method1(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void method2(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
//conn.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void method3(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
从Wireshark抓到的数据包分析来看,答案是1个TCP连接。
从上图,我观察到系统间建立连接之后(SYN,ACK)连续发送3个HTTP请求。所以即使是在不同的方法里面调用HttpURLConnection conn = (HttpURLConnection) url.openConnection() ;系统也只是创建一个连接。
那么,如果是2个线程会创建多少个连接呢?我把代码修改成如下,
package art.programming.net;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* see
* http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html
*
*
* @author Alex Chen
*/
public class HttpClient {
public static void main(String... args){
try{
final URL url = new URL("http://www.google.cn");
new Thread(){
public void run(){
HttpClient httpClient = new HttpClient();
httpClient.method1(url);
httpClient.method2(url);
httpClient.method3(url);
}
}.start();
new Thread(){
public void run(){
HttpClient httpClient = new HttpClient();
httpClient.method1(url);
httpClient.method2(url);
httpClient.method3(url);
}
}.start();
}catch(Exception e){
}
}
public void method1(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void method2(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
//conn.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void method3(final URL url){
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("HEAD");
int len = conn.getContentLength();
System.out.println("内容长度: " + len);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
观察到2个连接被建立(两个SYN,ACK),处理6次HTTP请求。同时观察到TCP连接没有被关闭(没有FIN标志位出现)。
结论
URL.openConnection并不是每次都会创建一个TCP连接。