起因
这两天在做有关网络的编程练习,发现网路已经联通,但是服务器端接收不到数据,排查了好久,功能一个个测试了之后,服务端的代码没有问题了.焦点落在客户端.最后通过查看源代码,发现原来是要按照严格的时序来进行操作.如下:
1. 创建URL实例;
2. 获取URLConnection 实例,如果单纯的只是从服务器读数据,直接从URL实例读就可以了;
3. setDoOutput
设置输出功能,这样就相当于POST
方式;
4. 打开链接服务器的一个连接,这一步其实可以省略;
5. getOutputStream()
获取输出流,此方法会隐式打开与服务端的连接;
6. write()
将数据写到输出流;
7. close()
关闭输出流;
8. getOutputStream()
获取输入流;
9. read()
从输入流读取服务端返回数据;
10. close()
关闭输入流.
这其中,第8步 必须 在第7步 之后,否则数据是无法传输到服务端的.因为在使用getOutputStream()
的同时,会将getOutputStream()
获取的输出流关闭,这就造成实际上并没有把数据写出去,因此服务器端自然无法接收到数据.
好了,TL;DR,如果这是为了知道原因,到这一步也就完了,下面的就没必要看了.
下面是源码分析:
URLConnection#openConnection()
位置:java/net/URL.java
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
}
//其中 handler 为
URLStreamHandler handler = handlers.get(protocol);
//Handlers 为HashTable的实例
static Hashtable<String,URLStreamHandler> handlers = new Hashtable<>();
//那么, 既然是HashTable , 有get 就会有put ,我们看看put 在哪?通过寻找发现,handlers.put() 仅仅有一处:在static URLStreamHandler getURLStreamHandler(String protocol)中
static URLStreamHandler getURLStreamHandler(String protocol) {
URLStreamHandler handler = handlers.get(protocol);
if (handler == null) {
boolean checkedWithFactory = false;
// Use the factory (if any)
if (factory != null) {
handler = factory.createURLStreamHandler(protocol);
checkedWithFactory = true;
}
// Try java protocol handler
if (handler == null) {
String packagePrefixList = null;
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
protocolPathProp,""));
if (packagePrefixList != "") {
packagePrefixList += "|";
}
// REMIND: decide whether to allow the "null" class prefix
// or not.
packagePrefixList += "sun.net.www.protocol";
StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, "|");
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {
String packagePrefix =
packagePrefixIter.nextToken().trim();
//通过反射获取实际的 URLConnection 的子类
String clsName = packagePrefix + "." + protocol +
".Handler";
Class<?> cls = null;
try {
cls = Class.forName(clsName);
} catch (ClassNo