java URL协议处理

URLConnection是一个协议处理器中的一个类,它是表示指向URL所指定的资源的活动连接。主要用于两个方面,一个是与服务器(特别是HTTP服务器)的交互,可以用来查看服务器发送的首部,设置连接的属性,设置客户端的请求的首部等。利用它也可以实现POST和PUT方法来发送数据。另一个方面是Java的协议处理器机制的一部分。所谓的协议处理器就是将处理协议的细节从处理特定数据类型中分离出,会涉及到客户端与服务器端的交互,如生成正确的请求格式,解释与数据一起返回的首部等。


获取URLConnection
   根据一个已经创建的URL来通过openConnection来打开生成一个URLConnection,虽然利用url来获取,其实内部调用的是URLStreamHandler的openConnection,该方法是一个抽象方法,它返回的URLConnection会根据协议的类型返回,倘若是一个http url则就会返回一个HTTPURLConnection。
   URL url=new URL("http://www.baidu.com");
   URLConnection uc=url.openConnection();
   此时该uc并没有连接,本地与远程主机是无法进行收发数据的,需要调用uc.connect()方法来建立连接。不过在使用getInputStream(),getHeaderField()等其它要求的时候会首先自动调用这个方法。
   利用此URLConnection可以很容易读取来自服务器端的数据,只要其getInputStream。

URL和URLConnection的区别
URLConnection提供了对于HTTP首部的访问
URLConnection可以配置客户端向服务器端发送的请求参数即首部

URLConnection可以利用POST,PUT等请求方法与服务器进行交互

利用URL与URLConnection都可以向服务器发送HTTP请求。

1,创建一个URL,传递一个字符串形式的HTTP请求,可以加上查询字符串,此时默认动作类型是get,也就是查询,从服务器返回一个资源,再通过getInputStream正式建立客户端与服务器之间的连接,将该HTTP请求发送到服务器端,建立必要的握手,由于HTTP是全双工的,此时就可以在这里面获取服务器的返回输出流。

2,通过url的opentConnection获取一个指向url地址的活动连接,初次获取的时候是没有连接的,本地和远程根本不能收发数据,也就是没有socket来连接这台主机。要利用connect()方法来在本地和远程主机之间建立一个TCP socket连接,不过一般都是在使用getInputstream自动调用这个方法,使用getInputStream才是真正的进行发送HTTP的请求消息。
读取服务器响应的首部
对于服务器响应的首部,当然这里都是以HTTP服务器响应为准,可以来获取响应中的首部字段。
Content-type,Content-length,Content-encoding,Date,Last-modified,Expires
可以利用getContentType()来获取数据的MIME类型,判断字符编码,并且以正常的格式显示出,如
 String encoding= "ISO-8859-1";//HTTP默认的编码方式
 URL u= new URL(url);
 URLConnection uc=u.openConnection();
  String type=uc.getContentType();
//获取字符编码方式
int star=type.indexOf( "charset=");
if(star!=-1)
          encoding=type.substring(star+8);
   System.out.println( "CharSet:"+encoding);
   InputStream raw=uc.getInputStream();
    Reader r= new InputStreamReader( new BufferedInputStream(raw),encoding);
利用getContentLength来获取二进制文件大小,从而来下载文件
URLConnection uc=url.openConnection();
   String contentType=uc.getContentType();
int contentLength=uc.getContentLength();
if(contentType.startsWith( "text/")||contentLength==-1)
   {
throw new IOException( "This is not a binary file");
   }

   InputStream raw= new BufferedInputStream(uc.getInputStream());
byte[] data= new byte[contentLength];

int bytesRead=0;
int offset=0;
while(offset<contentLength)
   {
     bytesRead=raw.read(data, offset, contentLength);
if(bytesRead==-1) break;
     offset+=bytesRead;
   }

   raw.close();

if(offset!=contentLength)
   {
throw new IOException( "Only read "+offset+  "bytes;Expected "+contentLength+ " bytes");
   }

//根据URL中获取文件路径的名,将获取的流写入到本地
   String file=url.getFile();
int start=file.lastIndexOf( "/");
   String filename=file.substring(start+1);

   FileOutputStream fout= new FileOutputStream(filename);
   fout.write(data);
   fout.flush();
   fout.close();
getHeaderField(String name);可以根据指定首部名来不区分大小写获取该名对应的值
getHeaderFieldKey(int n)和getHeaderField(int n)依此返回首部中的名和值,很有用
     URL u= new URL(url);
     URLConnection uc=u.openConnection();

for( int j=1;;j++)
     {
       String header=uc.getHeaderField(j);
if(header== null) break; //skip the loop
       System.out.println(uc.getHeaderFieldKey(j)+ ":"+header);
     }

配置连接
所谓的配置主要就是定义了客户端如何向服务器做出请求。
一般客户端在默认情况下doInput为true,表示可以接受来自服务器端发送的数据,这些必须在URLConnection连接之前设置。如果想要利用POST和PUT进行交互就必须要设置doOutpu为true,默认是false

配置客户端发送的HTPP首部
客户端进行服务器访问的时候,有些协议需要加上首部,配置一些名值对,可以通过setRequestProperty(name,value)设置,多个值之间利用逗号隔开。

客户端利用POST来发送数据
package com.urlconnection;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URL;
import java.net.URLConnection;

//模拟表单提交处理
public class FormPoster {

private URL url;
private QueryString query= new QueryString();

//必须要保证是http协议
public FormPoster(URL url)
 {
if(!url.getProtocol().toUpperCase().startsWith( "HTTP"))  
           {
throw new IllegalArgumentException( "Posting only works for http urls");
           }
this.url=url;
 }

public void add(String name,String value)
 {
   query.add(name, value);
 }

public URL getURL()
 {
return this.url;
 }

public InputStream post() throws IOException
 {
   URLConnection uc=url.openConnection();
   uc.setDoOutput( true);
   Writer out= new OutputStreamWriter(uc.getOutputStream(), "ASCII");
//POST行,Content-type和Content-length是由URLConnection发送的
//只需要发送数据即可
   out.write(query.toString());
   out.write( "\r\n\r\n");
   out.flush();
   out.close();

return uc.getInputStream();
 }

public static void main(String[] args) {
// TODO Auto-generated method stub
   String u= "http://www.baidu.com";
   URL url=null;
try{
       url=new URL(u);
   }catch(IOException e)
   {
   }
                FormPoster poster=new FormPoster(url);
                poster.add("hawk""fdafda");
                poster.add("good morning""fdafa");

try{
                   InputStream in=poster.post();

//读取响应
                   InputStreamReader r=new InputStreamReader(in);
int c;
while((c=r.read())!=-1)
                   {
                    System.out.print((char)c);
                   }
                   in.close();
                }catch(IOException ex)
                {
                   System.err.println(ex);
                }
 }

}

HTTPURLConnection
这个是一个专门操作http URL的类,继承子URLConnection,主要有7个请求的方法
利用setRequestMethod(String method)设置不同的请求方法,默认是get,区分大小写
GET 从服务器端获取数据
POST 向服务器端提交表单
PUT 上传文件至服务器
HEAD , 获取服务器端响应的头部
OPTIONS , 查询服务器支持哪些请求方法
DELETE , 删除服务器中的文件
TRACE 服务器返回客户端发送的HTTP首部,用于检测代理服务器对HTTP首部进行怎样修改
服务器响应的格式如下:
HTTP/1.1 200 OK
...
这个类添加了两个方法
getResponseMessage()获取响应码对应的消息,如OK
getResponseCode()获取响应码,如200

协议处理器
   协议处理器主要就是根据URL中的协议来找到合适的协议处理器来进行客户端与服务器端的交互。主要涉及四个类,具体类URL,抽象类URLConnection和URLStreamHandler,以及接口URLStreamHandlerFactory。协议处理器的流处理器总是根据指定的协议找到最适合的URLConnection
   要想自己建立协议处理器,需要编写两个 URLConnection和URLStreamHandler的子类,然后创建一个 URLStreamHandlerFactory。
URLConnection子类主要处理与服务器交互,将服务器发送的数据转换为InputStream,将客户端发送的所有数据转换为OutputStream。 URLStreamHandler子类主要将URL的字符串表示解析为各个部分,用这些部分来设置URL对象的各个部分,并且创建一个理解次URL协议的URLConnection。

协议处理器的基本流程如下
1 程序先利用字符串构建某一个协议的URL对象,在创建URL模式中,只会验证是否识别URL模式,而不会对其格式的正确性进行检查
2 构造函数利用所传递的参数来确定URL的协议部分,如http
3 URL()构造函数以如下方式尝试进行找到给定的 URLStreamHandler
  a 如果以前使用过此协议,从缓存中获取 URLStreamHandler
  b 否则,若设置了 URLStreamHandlerFactory,将此字符串传递给工厂
  c 若两个都没有,则尝试实例化一个位于java.protocol.handler.pkgs属性列出的                  protocol.Handler的 URLStreamHandler
  d 如果实例化失败,就尝试实例化一个位于sun.net.www.protocol中的 protocol.Handler            的 URLStreamHandler
  e 如果其中一个成功了,则设置属性字段handler字段。如果都不成功,则抛                        出 MalformedURLException异常
4 程序调用URL对象的openConnection方法
5 URL会根据协议让 URLStreamHandler返回一个适合于此URL的URLConnection
6 使用 URLConnection进行与远程资源的交互

URLStreamHandler解析URL中字段的过程
URL(String)-->URL(URL,String)-->URL(URL,String,String)--> URLStreamHandler.parseURL()
--> URLStreamHandler.setURL()--> URL.set();
要新建立一个 URLStreamHandler一般只需要修改openConnection方法即可,根据需要来修改parseURL
为每一个URLConnection子类重写一个connect方法,该方法包含了Socket的具体建立步骤。
再利用URLStreamHandlerFactory来注册这些流处理器
在信息交互系统设计中,不乏有自定义通讯协议设计。本章会介绍如何利用 java.net.URL 类来自定义协议。

       一般而言, URL 的格式是: protocol://[authority]hostname:port/resource?queryString 。 URL 类能够解析出 protocol、 hostname 、 port 等信息。 Protocol 决定了交互规范,通用的协议,比如 HTTP 、 File 、 FTP 等协议, JDK 自带了默认的通讯实现。当然,自定义实现是允许的。 Hostname 和 port 一般用于 Socket 或者基于 Socket 其他协议通讯方式。Resource 即资源上下文。可能读者利用 URL ,通过指定协议( protocol )来获取指定资源的读写,比如 JDK 内置了HTTP 、 File 、 FTP 等协议的处理方法。哪么 URL 的工作原理到底是怎么样的呢?

      在成功地构造 URL 实例之后, URL API 中定义了一个 openConnection() 方法,返回一个 java.net.URLConnection 抽象类型的实例。不过,这里 URL 对象是代理对象(组合了 其 对象),实际调用的是, java.net.URLStreamHandler 对象的 openConnection() 方法。

      java.net.URLStreamHandler 对象可以有两条途径得到:实现 java.net.URLStreamHandler ,或者实现java.net.URLStreamHandlerFactory 。

      java.net.URLStreamHandler 是一个工厂类,通过 openConnection(java.net.URL) 方法来创建 java.net.URLConnection的实例。 java.net.URLStreamHandler 实现灵活度很大,既可以通过不同 protocol 的 URL 实例,产生java.net.URLConnection 对象。还可以通过相同 protocol 的多个 URL 对象,来产生对象。通用性实现,一种协议对应一个java.net.URLStreamHandler 实现。比如,在 SUN JDK 中 sun.net.www.protocol 子包下面的多个 Handler 类就是很好的例子。如果读者有兴趣,可以去看看相关实现。

      1. 通过 java.net.URLStreamHandlerFactory 机制

      java.net.URLStreamHandlerFactory ,顾名思义,它是 java.net.URLStreamHandler 的工厂,即抽类工厂接口。通过调用 createURLStreamHandler(String protocol) 来创建 java.net.URLStreamHandler 对象。因此,建议java.net.URLStreamHandlerFactory 实现类应该采用 one protocol one hander 的模式, SUN JDK 也采用该模式。

     大致解了他们关系之后,再通过 UML Class diagram 来熟悉下:    

 

(图 1 

 

     图 1 所示, URL 包含了名为 factory 的 URLStreamHandlerFactory 类对象和 handler 的 URLStreamHandler 的实例对象。对于 URL 而言, handler 对象是必须的,因为前面说到实际处理 openConnection() 方法是 handler 对象,而 factory并不是必须的。接下来,来分析这两个对象是如何和 URL 交互的。

     在 URL 的构造方法中,暂时不用关心协议字符串等参数,更多的关注于 URL context 和 URLStreamHandler 参数。URL 实例能够依赖于 URL context ,当 URLStreamHandler 参数为空的情况下,当前 URL 实例将会采用 URL context 的URLStreamHandler 成员对象。当 Context 和 URLStreamHandler 参数都为空的时。 URL 会调用 getURLStreamHandler( String) 方法,从而根据协议 (protocol) 获得协议 URLStreamHandler 对象。

     在 URL 底层实现中,最初会初始化一个 protocol 和 hander 键值关系的 Map 映射。如果找到已有的映射关系,立即返回 URLStreamHandler 对象(第一次是取不到 URLStreamHandler 对象的)。

     如果找不到的话,并且 URL 类中的类成员 URLStreamHandlerFactory 实例不为空的情况下,这个实例通过URL#setURLStreamHandlerFactory 方法来注册。 getURLStreamHandler 方法会调用这个类成员的createURLStreamHandler(String) 方法来创建 URLStreamHandler 实例。

 

 

 

Java代码   收藏代码
  1. URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());  

(代码 1 

Java代码   收藏代码
  1. class MyURLStreamHandlerFactory implements URLStreamHandlerFactory{  
  2.         @Override  
  3.         public URLStreamHandler createURLStreamHandler(String protocol) {  
  4.             return null;  
  5.         }   
  6.           
  7. }  

(代码 2 

 

 

 

      当 createURLStreamHandler 方法返回 null 的时候, URL 的 getURLStreamHandler 方法会采用 URLStreamHandler处理机制。

 

 

      2. 通过 java.net.URLStreamHandler 机制

      2.1. 实现类包路径定义

        通过 JVM 启动参数 -D java.protocol.handler.pkgs 来设置 URLStreamHandler 实现类的包路径,例如 -Djava.protocol.handler.pkgs=com.acme.protocol , 代表处理实现类皆在这个包下。如果需要多个包的话,那么使用“ |” 分割。比如 -D java.protocol.handler.pkgs=com.acme.protocol|com.acme.protocol2 。 SUN 的 JDK 内部实现类均是在sun.net.www.protocol. 包下,不必设置。 路径下的协议实现类,采用先定义先选择的原则 。

      2.2. 实现类的命名模式

      类的命名模式为 [package_path].[protocol].Handler ,比如默认实现” sun.net.www.protocol.[protocol].Handler”, 比如HTTP 协议的对应的处理类名为 -sun.net. www.protocol.http.Handler 。同样,自定义实现的处理类,例如, JDNI 协议实现类命名 com.acme.protocol.jndi.Handler 。

      2.3. 实现类必须又默认构造器。

      结合代码分析,如下:

Java代码   收藏代码
  1. URL httpURL = new URL(null"http://www.google.com",URLStreamHandler)null);  
  2. URLConnection urlConn = httpURL.openConnection();  

 

(代码 3 

 

      代码 3 中没有配置 URLStreamHandler 的类,并且代码 2 中工厂类没有实现了 HTTP 协议。这样, URL 会获取默认的 HTTP 处理类 sun.net. www.protocol.http.Handler 

      Java 1.5 开始支持网络代理的操作,因此 URLStreamHandler 实现类尽量覆盖 openConnection(URL) openConnection(URL,Proxy) 两个方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值