URLConnection 的陷阱 java_关于connection reset 服务器cgi

 

image


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图:有些复杂,知道个大概.


image

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.

 

 

posted @ 2011-04-07 18:23  cubase01  阅读(12546)  评论(0编辑  收藏  举报