关于UrlConnection连接和Socket连接的区别,只知道其中的原理如下:
抽象一点的说,Socket只是一个供上层调用的抽象接口,隐藏了传输层协议的细节。
urlconnection 基于Http协议,Http协议是应用层协议,对传输层Tcp协议进行了封装,是无状态协议,不需要你去考虑线程、同步、状态管理等,内部是通过socket进行连接和收发数据的,不过一般在数据传输完成之后需要关闭socket连接。
直接使用Socket进行网络通信得考虑线程管理、客户状态监控等,但是不用发送头信息等,更省流量。
并不知道我们经常使用的URLConnection 内部是怎么实现的,今天心血来潮以URL为起点来探个究竟。
以下面这段代码为出发点
URL url = new URL("http://zhoujianghai.iteye.com");
URLConnection conecttion = (URLConnection)url.openConnection();
根据java.net.URL源码,一步步进行分析,
new URL("www.javaeye.com")会调用URL(URL context, String spec, URLStreamHandler handler),
此时context和handler是null。
url.openConnection()调用的是strmHandler.openConnection(this);
而strmHandler是URLStreamHandler接口的子类的实例。
抽象类 URLStreamHandler 是所有流协议处理程序的通用超类,可以通过不同 protocol 的 URL 实例,产生 java.net.URLConnection 对象。
由于context和handler是null,所以最终根据具体的协议调用URL类中的setupStreamHandler()方法对strmHandler进行初始化。
下面分析 setupStreamHandler()方法内的代码。
- String packageList = AccessController.doPrivileged(new PriviAction<String>(
- "java.protocol.handler.pkgs"));
String packageList = AccessController.doPrivileged(new PriviAction<String>( "java.protocol.handler.pkgs"));
首先通过java.protocol.handler.pkgs 来设置 URLStreamHandler 实现类的包路径,SUN 的 JDK 内部实现类均是在 sun.net.www.protocol. 包下。
关于sun/net/www/protocol/http包下相关类的源码,可以访问:http://www.docjar.org/docs/api/sun/net/www/protocol/http/package-index.html
PriviAction 部分源码:
- public PriviAction(String property) {
- action = GET_SYSTEM_PROPERTY;
- arg1 = property;
- }
- public T run() {
- switch (action) {
- case GET_SYSTEM_PROPERTY:
- return (T)System.getProperty((String) arg1, (String) arg2);
- case GET_SECURITY_PROPERTY:
- return (T)Security.getProperty((String) arg1);
- case GET_SECURITY_POLICY:
- return (T)Policy.getPolicy();
- case SET_ACCESSIBLE:
- ((AccessibleObject) arg1).setAccessible(true);
- }
- return null;
- }
public PriviAction(String property) { action = GET_SYSTEM_PROPERTY; arg1 = property; } public T run() { switch (action) { case GET_SYSTEM_PROPERTY: return (T)System.getProperty((String) arg1, (String) arg2); case GET_SECURITY_PROPERTY: return (T)Security.getProperty((String) arg1); case GET_SECURITY_POLICY: return (T)Policy.getPolicy(); case SET_ACCESSIBLE: ((AccessibleObject) arg1).setAccessible(true); } return null; }
PriviAction<T> 实现了 java.security.PrivilegedAction<T>接口,会执行run()方法。
由action = GET_SYSTEM_PROPERTY;可知会执行代码:
- (T)System.getProperty((String) arg1, (String) arg2);
(T)System.getProperty((String) arg1, (String) arg2);
System.getProperty方法调用成员变量Properties props的getProperty(key, def)方法;
由于并未设置任何Properties,此处会返回默认值def。def的值是null。
因此会继续执行下面的代码:
- String className = "org.apache.harmony.luni.internal.net.www.protocol." + protocol
- + ".Handler";
- try {
- strmHandler = (URLStreamHandler) Class.forName(className)
- .newInstance();
- } catch (IllegalAccessException e) {
- } catch (InstantiationException e) {
- } catch (ClassNotFoundException e) {
- }
String className = "org.apache.harmony.luni.internal.net.www.protocol." + protocol + ".Handler"; try { strmHandler = (URLStreamHandler) Class.forName(className) .newInstance(); } catch (IllegalAccessException e) { } catch (InstantiationException e) { } catch (ClassNotFoundException e) { }
此时protocol是http协议,从而根据协议 (protocol) 获得协议 URLStreamHandler 对象。
所以此时通过反射机制创建org.apache.harmony.luni.internal.net.www.protocol.http.Handler类的实例。
此时回到上面提到的 strmHandler.openConnection(this);
将调用下面的Handler类的实例的openConnection(URL u)方法。
@Override
- protected URLConnection openConnection(URL u) throws IOException {
- return new HttpURLConnectionImpl(u, getDefaultPort());
- }
- ected URLConnection openConnection(URL u, Proxy proxy)
- throws IOException {
- if (null == u || null == proxy) {
- throw new IllegalArgumentException(Messages.getString("luni.1B"));
- }
- return new HttpURLConnectionImpl(u, getDefaultPort(), proxy);
- }
protected URLConnection openConnection(URL u) throws IOException { return new HttpURLConnectionImpl(u, getDefaultPort()); } protected URLConnection openConnection(URL u, Proxy proxy) throws IOException { if (null == u || null == proxy) { throw new IllegalArgumentException(Messages.getString("luni.1B")); } return new HttpURLConnectionImpl(u, getDefaultPort(), proxy); }
此时会创建HttpURLConnectionImpl对象。HttpURLConnectionImpl是java.net.HttpURLConnection的子类。 该类有个成员变量HttpConnection connection;
这才是我们要找的,该类对Socket进行了封装。
HttpConnection 部分源码如下:
private Socket socket;
- private SSLSocket sslSocket;
- private InputStream inputStream;
- private OutputStream outputStream;
- private InputStream sslInputStream;
- private OutputStream sslOutputStream;
- private HttpConfiguration config;
- public HttpConnection(HttpConfiguration config, int connectTimeout) throws IOException {
- this.config = config;
- String hostName = config.getHostName();
- int hostPort = config.getHostPort();
- Proxy proxy = config.getProxy();
- if(proxy == null || proxy.type() == Proxy.Type.HTTP) {
- socket = new Socket();
- } else {
- socket = new Socket(proxy);
- }
- socket.connect(new InetSocketAddress(hostName, hostPort), connectTimeout);
- }
private SSLSocket sslSocket; private InputStream inputStream; private OutputStream outputStream; private InputStream sslInputStream; private OutputStream sslOutputStream; private HttpConfiguration config; public HttpConnection(HttpConfiguration config, int connectTimeout) throws IOException { this.config = config; String hostName = config.getHostName(); int hostPort = config.getHostPort(); Proxy proxy = config.getProxy(); if(proxy == null || proxy.type() == Proxy.Type.HTTP) { socket = new Socket(); } else { socket = new Socket(proxy); } socket.connect(new InetSocketAddress(hostName, hostPort), connectTimeout); }
现在UrlConnection连接和Socket连接的区别应该十分清楚了吧。
使用UrlConnection比直接使用Socket要简单的多,不用关心状态和线程管理。
UrlConnection基于Http协议,只是进行了封装,添加了一些额外规则(如头信息),本质上也是建立TCP连接,利用Socket实现连接和传输数据的,不过我们一般每次请求完数据后都会实现finally方法,在该方法里关闭连接。