java.net.ProtocolException: Can't reset method: already connected at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:314) at MultiDowloader.run(MultiDowloader.java:33)
今天写多线程下载的时候总是遇到以上的问题.查阅很多资料一无所获.看到javaworld上有一篇03/23/01写的文章..(http://www.javaworld.com/javaworld/jw-03-2001/jw-0323-traps.html?page=1)介绍了这一方面的问题.我加上自己的白话再说一遍吧.
在以post访问服务器时,java过于简化访问的复杂性,全揉在了一起.
先一个uml图:有些复杂,知道个大概.
URLConnection 这个类是一个抽象类,我们只能通过URL这个类来得到URLConnection的引用.
先看左上角的URL类,分为以下几步:
1.当URL这个类创建时,会有一个URLStreamHandlerFactory的引用产生.
2.URLStreamHandlerFactory会创建一个专门处理网络协议的类 URLStreamHandler,
3.而URLStreamHandler又会去初始化URLConnection,这个类专门负责打开URL里面的连接.
4.URLStreamHandler又将生成一个ContentHandler的实例处理URL请求到内容.比如gif rar 之类的文件.
我想.....转到这.你已经晕了..这个java的网络连接模型设计的极不合理.在Design of Everyday Things (Doubleday, 1990), Donald Norman 就说过:最好的设计是让我们能预测我们行为的后果.这基本上是背道而驰 .
以上设计存在这几方面的缺陷:
1.URL这个类名跟这个类所做的事完全不相称..我们生活中所说的URL,妇孺皆知...http://www.....但这里的URL 管了太多事.
2.URL这个类太偏向于接收数据而非写入数据.
3.调用protocol handlers 让人困惑.因为它的主要责任应该是建立连接.而不是处理流数据.
URLConnection 和socket 一样有类似的getOutputStream()与getInputStream()方法,当你理所当然的像编写soket一样来处理URLConnection ,应该会理所当然的得到以下代码:
import java.net.*;
import java.io.*;
public class BadURLPost
{
public static void main(String args[])
{
// get an HTTP connection to POST to
try
{
// get the url as a string
String surl ="http://www.foshanshop.net/ejb3/ActivePort.exe"; URL url = new URL(surl);
URLConnection con = url.openConnection();
System.out.println("Received a : " + con.getClass().getName());
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
String msg = "Hi HTTP SERVER! Just a quick hello!";
con.setRequestProperty("CONTENT_LENGTH", "5"); // Not checked
con.setRequestProperty("Stupid", "Nonsense");
System.out.println("Getting an input stream...");
InputStream is = con.getInputStream();
System.out.println("Getting an output stream...");
OutputStream os = con.getOutputStream();
/*
con.setRequestProperty("CONTENT_LENGTH", "" + msg.length());
Illegal access error - can't reset method.
*/
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write(msg);
osw.flush();
osw.close();
System.out.println("After flushing output stream. ");
// any response?
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
{
System.out.println("line: " + line);
}
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
非常不幸..你将会得到以下错误:
Getting an input stream...
Getting an output stream...
java.net.ProtocolException: Can't reset method: already connected
at
java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:10
2)
at
sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLCo
nnection.java:349)
at
com.javaworld.jpitfalls.article2.BadURLPost.main(BadURLPost.java:38)
这个类有两个小问题:
setRequestProperty() 没有做值的安全检查,但还是会向服务器发送.
在发送完后,setRequestProperty()还是可以随意调用..这不符合常理.
下面:我们交换getInputStream() 和getOutputStream()的调用顺序.
import java.net.*;
import java.io.*;
public class BadURLPost
{
public static void main(String args[])
{
// get an HTTP connection to POST to
try
{
// get the url as a string
String surl ="http://www.foshanshop.net/ejb3/ActivePort.exe";
URL url = new URL(surl);
URLConnection con = url.openConnection();
System.out.println("Received a : " + con.getClass().getName());
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
String msg = "Hi HTTP SERVER! Just a quick hello!";
con.setRequestProperty("CONTENT_LENGTH", "5"); // Not checked
con.setRequestProperty("Stupid", "Nonsense");
System.out.println("Getting an output stream...");
OutputStream os = con.getOutputStream();
System.out.println("Getting an input stream...");
InputStream is = con.getInputStream();
/*
con.setRequestProperty("CONTENT_LENGTH", "" + msg.length());
Illegal access error - can't reset method.
*/
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write(msg);
osw.flush();
osw.close();
System.out.println("After flushing output stream. ");
// any response?
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
{
System.out.println("line: " + line);
}
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
你会发现,不会报错.(某些地址报错是因为验证机制),但返回的数据为空.
所以,很明显getInputStream() 是向服务器写数据的关键方法,所以要想正确运行,我们必须(open output, write, open input, read)
import java.net.*;
import java.io.*;
public class GoodURLPost
{
public static void main(String args[])
{
// get an HTTP connection to POST to
try
{
// get the url as a string
String surl = "http://www.foshanshop.net/ejb3/ActivePort.exe";
URL url = new URL(surl);
URLConnection con = url.openConnection();
System.out.println("Received a : " + con.getClass().getName());
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
String msg = "Hi HTTP SERVER! Just a quick hello!";
con.setRequestProperty("CONTENT_LENGTH", "" + msg.length());
// Not checked
System.out.println("Msg Length: " + msg.length());
System.out.println("Getting an output stream...");
OutputStream os = con.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write(msg);
osw.flush();
osw.close();
System.out.println("After flushing output stream. ");
System.out.println("Getting an input stream...");
InputStream is = con.getInputStream();
// any response?
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
{
System.out.println("line: " + line);
}
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
以上将完美运行....ok.