URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。而URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。而URN,uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI。
我们很多时候把URL认为是只与网络相关,但是确切的说,网络资源定位只是他的一个(资源定位工厂)子集。
一.序言一段
我们习惯了http
URL url=new URL("http://www.apptest.com:8080/test/ios.php");
我们也要习惯
"https", "ftp", "mailto", "telnet", "file", "ldap", "gopher", "jdbc", "rmi", "jndi", "jar", "doc", "netdoc", "nfs", "verbatim", "finger", "daytime", "systemresource"
当然,我们还要让URL习惯我们
URL url=new URL("oschina://www.apptest.com:8080/test/ios.php");
如果不习惯,总会出现如下异常
java.net.MalformedURLException: unknown protocol
在Android浏览器使用Ajax时也会不支持没有定义的过的协议。
二.协议的自定义
协议:在编程的世界里,协议本身就是一套Input/ouput约束规则,因此,我们确切的协议应该围绕I/O展开的,所以,这里的协议可以称为I/O协议。
协议发起方:request
协议响应方:response
协议成立的条件是:request和reponse认可同一套协议,并按照协议约束进行通信。
三.自定义协议与URL
在java中,自定义协议一定需要用URL吗?
答案是否定的。
事实上,围绕I/O,我们的规则定义完全有我们本身掌握,并没有说离开URL地球不转了,Java要毁灭了。
为什么使用URL类来自定义协议?
答案是因为URL是一套成熟的协议通信处理框架。
这里说的自定义URL协议,实质上更多的是通过已有的规则进行扩充协议。
四.实战
我们知道,自定义协议需要Response 和Request,双方需要充理解对方的协议。这里为了方便起见,我们使用Http协议服务器来作为Response。
这里我们使用了Ngnix服务器+PHP+FastCGI来构建Reponse,部署代码如下
1.定义Response
<?php $raw_post_data = file_get_contents('php://input', 'r'); echo "-------/$_POST------------------/n<br/>"; echo var_dump($_POST) . "/n"; echo "-------php://input-------------/n<br/>"; echo $raw_post_data . "/n<br/>"; $rs = json_encode($_SERVER); file_put_contents('text.html',$rs); echo '写入成功';
2.定义Request
2.1实现URLStreamHandlerFactory工厂,主要用来产生协议处理器
public class EchoURLStreamHandlerFactory implements URLStreamHandlerFactory { public URLStreamHandler createURLStreamHandler(String protocol){ if(protocol.equals("echo") || protocol.equals("oschina")) { return new EchoURLStreamHandler(); } return null; } }
2.2实现URLStreamHandler,主要作用是生成协议对应的连接器
public class EchoURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL u) throws IOException { return new EchoURLConnection(u); } }
2.3实现URLConnection,作用是协议通信规则的自定义,这里我们使用HTTP协议作为通信规则
public class EchoURLConnection extends URLConnection { private Socket connection = null; public final static int DEFAULT_PORT = 80; public EchoURLConnection(URL url) { super(url); } public void sendHeader() throws UnsupportedEncodingException, IOException { BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(getOutputStream(), "UTF8")); ; wr.write("POST " + getURL().getPath() + " HTTP/1.1 /r/n"); wr.write("Host:www.java2000.net/r/n"); wr.write("Connection:keep-alive/r/n"); wr.write("Date:Fri, 22 Apr 2016 13:17:35 GMT/r/n"); wr.write("Vary:Accept-Encoding/r/n"); wr.write("Content-Type: application/x-www-form-urlencoded/r/n"); wr.write("/r/n/r/n"); // 以空行作为分割 wr.flush(); } public synchronized InputStream getInputStream() throws IOException { if (!connected) { connect(); sendHeader(); } return connection.getInputStream(); } public synchronized OutputStream getOutputStream() throws IOException { if (!connected) { connect(); sendHeader(); } return connection.getOutputStream(); } public String getContentType() { return "text/plain"; } public synchronized void connect() throws IOException { if (!connected) { int port = url.getPort(); if (port < 0 || port > 65535) port = DEFAULT_PORT; this.connection = new Socket(url.getHost(), port); this.connected = true; } } public synchronized void disconnect() throws IOException { if (connected) { this.connection.close(); this.connected = false; } } }
在这里,协议定义已经完成。
我们测试代码如下
尝试连接 oschina://localhost:8080/test/ios.php
//注册协议工厂 URL.setURLStreamHandlerFactory(new EchoURLStreamHandlerFactory()); URL url=new URL("oschina://localhost:8080/test/ios.php"); EchoURLConnection connection=(EchoURLConnection)url.openConnection(); connection.setDoOutput(true); connection.setDoInput(true); InputStream stream = connection.getInputStream(); int len = -1; byte[] buf = new byte[1024]; while((len=stream.read(buf, 0, 1024))!=-1) { System.out.println(new String(buf, 0, len)); }
运行结果
结果说明,协议确实定义成功了
五.自定义mineType解析器
java中提供了 ContentHandlerFactory,用来解析mineType
public class EchoContentHandlerFactory implements ContentHandlerFactory{ public ContentHandler createContentHandler(String mimetype){ if(mimetype.equals("text/html") || mimetype.equals("text/plain")){ return new EchoContentHandler(); }else{ return null; } } }
public class EchoContentHandler extends ContentHandler { public Object getContent(URLConnection connection) throws IOException { InputStream in = connection.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); return br.readLine(); } public Object getContent(URLConnection connection, Class[] classes) throws IOException { InputStream in = connection.getInputStream(); for (int i = 0; i < classes.length; i++) { if (classes[i] == InputStream.class) return in; else if (classes[i] == String.class) return getContent(connection); } return null; } }
用法很简单
URLConnection.setContentHandlerFactory(new EchoContentHandlerFactory());