URL编程
URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上某一资源的地址。通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。
URL的基本结构由5部分组成:
<传输协议>://<主机名>:<端口号>/<文件名>
例如: http://192.168.1.100:8080/helloworld/index.jsp
public void testURL() throws IOException{
URL url = new URL("http://127.0.0.1:8080/examples/hello.txt?id=10");
System.out.println(url.getProtocol()); // 获取URL协议名:http
System.out.println(url.getHost()); // 获取URL主机名:127.0.0.1
System.out.println(url.getPort()); // 获取URL端口号: 8080
System.out.println(url.getPath()); // 获取URL文件路径:/examples/hello.txt
System.out.println(url.getFile()); // 获取URL文件名:/examples/hello.txt?id=10
System.out.println(url.getRef()); // 获取URL在文件中的相对位置:null
System.out.println(url.getQuery()); // 获取URL查询名:id=10
}
http请求
public static String doPostProcessHttp(String urlAddr, Map<String, String> map, String inputCharset) {
HttpURLConnection conn = null;
InputStream is = null;
OutputStreamWriter out = null;
String returnStr = null;
StringBuffer params = new StringBuffer();
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> element = it.next();
String value = element.getValue();
params.append(element.getKey());
params.append("=");
params.append(value);
params.append("&");
}
if (params.length() > 0) {
params.deleteCharAt(params.length() - 1);
}
try {
//和指定站点建立连接 既可以向指定站点获取资源 也可以发送资源
URL url = new URL(urlAddr);
//此处的urlConnection对象实际上是根据URL的请求协议(此处是http)生成的URLConnection类 的子类 HttpURLConnection,故此处最好将其转化为
//HttpURLConnection类型的对象,以便用到HttpURLConnection更多的API.如下
conn = (HttpURLConnection) url.openConnection();
//设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在 http正文内,因此需要设为true, 默认情况下是false;
conn.setDoOutput(true);
//设置是否从httpUrlConnection读入,默认情况下是true;
conn.setDoInput(true);
//设定请求的方法为"POST",默认是GET
conn.setRequestMethod("POST");
//Post 请求不能使用缓存
conn.setUseCaches(false);
// 设定传送的内容类型是可序列化的java对象 (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能java.io.EOFException)
conn.setRequestProperty("Content-type", "application/x-java-serialized- object");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-Length", String.valueOf(params.length()));
//连接主机的超时时间
conn.setConnectTimeout(500000000);
//从主机读取数据的超时时间
conn.setReadTimeout(500000000);
//连接,从上述url.openConnection()至此的配置必须要在connect()之前完成,
conn.connect();
//现在通过输出流对象构建对象输出流对象,以实现输出可序列化的对象。
out = new OutputStreamWriter(conn.getOutputStream(), inputCharset);
//向对象输出流写出数据,这些数据将存到内存缓冲区中
out.write(params.toString());
//刷新对象输出流,将任何字节都写入潜在的流中
out.flush();
//关闭流对象。此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区中,
//在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器
out.close();
//调用HttpURLConnection连接对象的getInputStream()函数,将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端,
//此处getInputStream会隐含的进行connect()(即:如同调用上面的connect()方法, 所以在开发中不调用上述的connect()也可以)
is = conn.getInputStream(); // <===注意,实际发送请求的代码段就在这里
//获取请求响应结果
BufferedReader br = new BufferedReader(new InputStreamReader(is, inputCharset));
StringBuffer retCodeBuffer = new StringBuffer();
while ((returnStr = br.readLine()) != null) {
retCodeBuffer.append(returnStr);
}
is.close();
conn.disconnect();
returnStr = retCodeBuffer.toString();
} catch (MalformedURLException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (ProtocolException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (UnsupportedEncodingException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (IOException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} finally {
try {
if (is != null) is.close();
if (out != null) out.close();
if (conn != null) conn.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
returnStr = null;
}
}
if (returnStr != null) {
returnStr = returnStr.trim();
}
return returnStr;
}
https请求
public static String doPostProcessHttps(String urlAddr, Map<String, String> map, String inputCharset) {
HttpURLConnection conn = null;
InputStream is = null;
OutputStreamWriter out = null;
String returnStr = null;
StringBuffer params = new StringBuffer();
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> element = it.next();
String value = element.getValue();
params.append(element.getKey());
params.append("=");
params.append(value);
params.append("&");
}
if (params.length() > 0) {
params.deleteCharAt(params.length() - 1);
}
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化 实现三个空方法 用来绕过证书
TrustManager[] tm = {new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(urlAddr);
//conn = (HttpURLConnection) url.openConnection();
HttpsURLConnection conn1 = (HttpsURLConnection) url.openConnection();
conn1.setSSLSocketFactory(ssf);
conn1.setDoOutput(true);
conn1.setRequestMethod("POST");
conn1.setUseCaches(false);
conn1.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn1.setRequestProperty("Content-Length", String.valueOf(params.length()));
conn1.setDoInput(true);
conn1.setConnectTimeout(600000);
conn1.setReadTimeout(600000);
conn1.connect();
out = new OutputStreamWriter(conn1.getOutputStream(), inputCharset);
out.write(params.toString());
out.flush();
out.close();
is = conn1.getInputStream(); //实际发送请求的代码段就在这里
BufferedReader br = new BufferedReader(new InputStreamReader(is, inputCharset));
StringBuffer retCodeBuffer = new StringBuffer();
while ((returnStr = br.readLine()) != null) {
retCodeBuffer.append(returnStr);
}
is.close();
conn1.disconnect();
returnStr = retCodeBuffer.toString();
} catch (MalformedURLException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (ProtocolException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (UnsupportedEncodingException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (IOException e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} catch (Exception e) {
LOG.info("doPostProcess error >>>> " + e.getMessage(), e);
returnStr = null;
} finally {
try {
if (is != null) is.close();
if (out != null) out.close();
if (conn != null) conn.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
returnStr = null;
}
}
if (returnStr != null) {
returnStr = returnStr.trim();
}
return returnStr;
}
/**
* 证书信任管理器(用于https请求)
* 在JSSE中,证书信任管理器类就是实现了接口X509TrustManager的类。
* 我们可以自己实现该接口,让它信任我们指定的证书。
*/
public class MyX509TrustManager implements X509TrustManager {
//该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,
//因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
//该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。
//在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
//返回受信任的X509证书数组。
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
总结
a:
HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。
b:
在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重, 对connection对象的一切配置(那一堆set函数)都必须要在connect()函数执行之前完成。而对outputStream的写操作,又必须要在inputStream的读操作之前。这些顺序实际上是由http请求的格式 决定的。如果inputStream读操作在outputStream的写操作之前,会抛出例外:java.net.ProtocolException: Cannot write output after reading input.......
c:
http请求实际上由两部分组成,一个是http头,所有关于此次http请求的配置都在http头里面定义, 一个是正文content。connect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前,就必须把所有的配置准备好。
d:
在http头后面紧跟着的是http请求的正文,正文的内容是通过outputStream流写入的,实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络, 而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。至此,http请求的东西已经全部准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http 请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数 之后对connection对象进行设置(对http头的信息进行修改)或者outputStream(对正文进行修改)都是没有意义的了,执行这些操作会导致异常的发生。