Socket编程笔记
android中的socket连接HttpURLConnection来实现的。而HttpClient是对HttpURLConnection做了一层封装,HttpClient6.0之后被废弃了,推荐用HttpURLConnection。而HttpURLConnection继承于HttpConnection,两者都是抽象类,想了解其中的实现原理,就要深入的剖析一下代码,但是其中一些关键的方法如connect()方法却是抽象的,所以需要找到它的实现类。
首先,是找到connect()的实现,最可能的是在URL类中找线索,因为是在url中获取的
URL url = new URL(httpUrl);//此处有异常抛出
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
然而,URL中却显示streamHandler中取得的
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
}
而streamHandler这个实例所属的对象实现的方法仍然是抽象的
protected abstract URLConnection openConnection(URL u) throws IOException;
也就是说单独知道这个出处还不够,还需要知道streamHandler是在什么时候被赋值的,这时候可以注意到URL中有URLStreamHandlerFactory streamHandlerFactory的工厂声明,点击去发现还是抽象的(晕),然后顺着streamHandlerFactory就找到了下面的方法,是对streamHandler赋值的关键
void setupStreamHandler() {
// Check for a cached (previously looked up) handler for
// the requested protocol.
streamHandler = streamHandlers.get(protocol);
if (streamHandler != null) {
return;
}
// If there is a stream handler factory, then attempt to
// use it to create the handler.
if (streamHandlerFactory != null) {
streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
return;
}
}
// Check if there is a list of packages which can provide handlers.
// If so, then walk this list looking for an applicable one.
String packageList = System.getProperty("java.protocol.handler.pkgs");
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (packageList != null && contextClassLoader != null) {
for (String packageName : packageList.split("\\|")) {
String className = packageName + "." + protocol + ".Handler";
try {
Class<?> c = contextClassLoader.loadClass(className);
streamHandler = (URLStreamHandler) c.newInstance();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
return;
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (ClassNotFoundException ignored) {
}
}
}
// Fall back to a built-in stream handler if the user didn't supply one
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
try {
String name = "com.android.okhttp.HttpHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("https")) {
try {
String name = "com.android.okhttp.HttpsHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
}
看注释区分,总共分为4个部分
第一部分
streamHandler = streamHandlers.get(protocol);
if (streamHandler != null) {
return;
}
是取缓存的streamHandler实例,如果已经存在了,就不会往下执行了。
第二部分
if (streamHandlerFactory != null) {
streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
return;
}
}
明显用到了工厂,所以需要知道工厂的出处,是这里:
/**
* Sets the stream handler factory for this VM.
*
* @throws Error if a URLStreamHandlerFactory has already been installed
* for the current VM.
*/
public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory factory) {
if (streamHandlerFactory != null) {
throw new Error("Factory already set");
}
streamHandlers.clear();
streamHandlerFactory = factory;
}
得了,这个是公共的设置方法:Sets the stream handler factory for this VM顿时感觉好高大上,总之,不通。
第三部分
在系统属性指定的包中寻找相应的URLStreamHandler处理类,因为没怎么用过这种方法,值得注意,不过不是这里的重点
String packageList = System.getProperty("java.protocol.handler.pkgs");
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (packageList != null && contextClassLoader != null) {
for (String packageName : packageList.split("\\|")) {
String className = packageName + "." + protocol + ".Handler";
try {
Class<?> c = contextClassLoader.loadClass(className);
streamHandler = (URLStreamHandler) c.newInstance();
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
return;
} catch (IllegalAccessException ignored) {
} catch (InstantiationException ignored) {
} catch (ClassNotFoundException ignored) {
}
}
}
第四部分
再不成只能做最后的挣扎了
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
try {
String name = "com.android.okhttp.HttpHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("https")) {
try {
String name = "com.android.okhttp.HttpsHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
其中https依然是在特定包com.android.okhttp.HttpsHandler中找
而其他的则是直接new一个对象,这些对象的导入
import libcore.net.url.FileHandler;
import libcore.net.url.FtpHandler;
import libcore.net.url.JarHandler;
在androidstudio中显示的是灰色的,表示没有没有提供相关源码,至此,线索全断了,不过通过这些查找也并非没有收获。
一直以来找的URLStreamHandler streamHandler其实是一个协议流处理器,每一个
对应一个URLHttpConnection,至于实现它们的源码,限于sdk中源码包没有所以没看到,不过却找到了出处,网上查了一下知道了其中实现也是通过Socket,大体的关系总是理清了。
总结:
1.URLHttpConnection和URLHttpsConnection都继承了URLConnection都是基于应用层对http或https协议的封装,里面通信还是用的socket
2.URLConnection里面调用的Socket是传输层对应用层提供的抽象接口,是下层黑箱的一个门面,其中包括的协议有传输层的TCP协议和UDP协议,以及网络层的IP协议。当然也可以直接用socket通信,可以在应用层通过socket来架构网络框架,但是需要考虑多线程,以及状态监控(其实用URLConnection也要自己写多线程)等因素,更省流量以及更可控,但是如果要用到http等应用层协议,还是用URLConnection方便些。
3.每个URLConnection都有一个相对应的URLStreamHandler实例用来处理协议,URL类中还有关于file,ftp,jar对应类型的URLStreamHandler应该也是一些协议的封装的处理器,也就是说每一种协议需要对应一种处理模型。