• 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.

  • 相关阅读:
    MVC异常过滤器
    文件分块传输
    UDP广播
    React 还是 Vue: 你应该选择哪一个Web前端框架?
    一个很好的XLSX的操作
    报表神器
    pycharm快敏捷键
    xlwt
    常用的列表和元祖
    HTML,css
  • 原文地址:https://www.cnblogs.com/syncg/p/2008242.html
Copyright © 2020-2023  润新知