1、http协议(这一块儿有时间的话会做记录)
2、常用的两种RPC方式
- 基于http协议:HttpClient和JDK自己的Http操作类
- 基于TCP或UDP协议:mina2和netty(这一部分以后有时间做记录)
3、HttpClient工具类的编写(只列出了两个最常用的方法get和post)
使用场合:我们可以在网页发送get或post请求去访问服务器server1,那我们在Java程序中想要模拟网页向服务器server1发送get和post请求的时候怎么办?--用HttpClient
版本:httpClient4.2.6(jar或者maven坐标自己加上)
maven坐标:
1 <dependency> 2 <groupId>org.apache.httpcomponents</groupId> 3 <artifactId>httpclient</artifactId> 4 <version>4.2.6</version> 5 </dependency>
代码实现(两个类):
MyX509TrustManager(自定义的信任管理器)
1 package com.util; 2 3 import java.security.cert.CertificateException; 4 import java.security.cert.X509Certificate; 5 6 import javax.net.ssl.X509TrustManager; 7 8 /** 9 * 自定义的信任管理器 10 */ 11 public class MyX509TrustManager implements X509TrustManager { 12 /** 13 * 检查客户端证书,若不信任,抛出异常 14 */ 15 public void checkClientTrusted(X509Certificate[] arg0, String arg1) 16 throws CertificateException { 17 } 18 /** 19 * 检查服务端证书,若不信任,抛出异常,反之,若不抛出异常,则表示信任(所以,空方法代表信任所有的服务端证书) 20 */ 21 public void checkServerTrusted(X509Certificate[] arg0, String arg1) 22 throws CertificateException { 23 } 24 /** 25 * 返回受信任的X509证书数组 26 */ 27 public X509Certificate[] getAcceptedIssuers() { 28 return null; 29 } 30 }
HttpClientUtil:
1 package com.util; 2 3 import java.io.IOException; 4 import java.security.KeyManagementException; 5 import java.security.NoSuchAlgorithmException; 6 import java.security.NoSuchProviderException; 7 import java.security.SecureRandom; 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.Properties; 12 import java.util.Set; 13 14 import javax.net.ssl.SSLContext; 15 import javax.net.ssl.TrustManager; 16 17 import org.apache.commons.collections4.MapUtils; 18 import org.apache.http.HttpEntity; 19 import org.apache.http.HttpResponse; 20 import org.apache.http.HttpStatus; 21 import org.apache.http.HttpVersion; 22 import org.apache.http.NameValuePair; 23 import org.apache.http.StatusLine; 24 import org.apache.http.client.ClientProtocolException; 25 import org.apache.http.client.HttpClient; 26 import org.apache.http.client.entity.UrlEncodedFormEntity; 27 import org.apache.http.client.methods.HttpGet; 28 import org.apache.http.client.methods.HttpPost; 29 import org.apache.http.client.utils.URLEncodedUtils; 30 import org.apache.http.conn.scheme.PlainSocketFactory; 31 import org.apache.http.conn.scheme.Scheme; 32 import org.apache.http.conn.scheme.SchemeRegistry; 33 import org.apache.http.conn.ssl.SSLSocketFactory; 34 import org.apache.http.impl.client.DefaultHttpClient; 35 import org.apache.http.impl.conn.PoolingClientConnectionManager; 36 import org.apache.http.message.BasicNameValuePair; 37 import org.apache.http.params.BasicHttpParams; 38 import org.apache.http.params.CoreConnectionPNames; 39 import org.apache.http.params.CoreProtocolPNames; 40 import org.apache.http.params.HttpParams; 41 import org.apache.http.util.EntityUtils; 42 43 /** 44 * 对HTTPClient的封装 45 */ 46 public class HttpClientUtil { 47 48 private static final String ENCODING = "UTF-8"; 49 50 private static HttpClient client = null; 51 private static SchemeRegistry schemeRegistry; //协议控制 52 private static PoolingClientConnectionManager ccm; //HttpClient连接池(多连接的线程安全的管理器) 53 54 static { 55 try { 56 /* 57 * 与https请求相关的操作 58 */ 59 SSLContext sslContext = SSLContext.getInstance("SSL","SunJSSE"); 60 sslContext.init(null, new TrustManager[]{new MyX509TrustManager()}, new SecureRandom()); 61 SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext); 62 /* 63 * 定义访问协议 64 */ 65 schemeRegistry = new SchemeRegistry(); 66 schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));//http 67 schemeRegistry.register(new Scheme("https", 443, socketFactory));//https 68 } catch (NoSuchAlgorithmException e) { 69 e.printStackTrace(); 70 } catch (NoSuchProviderException e) { 71 e.printStackTrace(); 72 } catch (KeyManagementException e) { 73 e.printStackTrace(); 74 } 75 76 Properties props = FileUtil.loadProps("http.properties");//加载属性文件 77 78 // 连接池管理 79 ccm = new PoolingClientConnectionManager(schemeRegistry); 80 ccm.setDefaultMaxPerRoute(FileUtil.getInt(props, "httpclient.max.conn.per.route", 20));//每个路由的最大连接数 81 ccm.setMaxTotal(FileUtil.getInt(props, "httpclient.max.conn.total", 400));//最大总连接数 82 83 HttpParams httpParams = new BasicHttpParams(); 84 httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, FileUtil.getInt(props, "httpclient.max.conn.timeout", 1000));//连接超时时间(ms) 85 httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, FileUtil.getInt(props, "httpclient.max.socket.timeout", 2000));//操作超时时间(ms) 86 httpParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_1);//设置http1.1或http1.0 87 88 client = new DefaultHttpClient(ccm, httpParams);//一个客户端就有一个连接池 89 } 90 91 /** 92 * get请求 93 * @param url 请求URL 94 * @param paramMap 请求参数 95 * @param headerMap 请求头信息 96 */ 97 public static String get(String url, 98 Map<String, String> paramMap, 99 Map<String, String> headerMap) throws ClientProtocolException, 100 IOException { 101 /* 102 * 拼接URL与参数 103 */ 104 if (MapUtils.isNotEmpty(paramMap)) { 105 List<NameValuePair> params = new ArrayList<NameValuePair>(); 106 for (String key : paramMap.keySet()) { 107 params.add(new BasicNameValuePair(key, paramMap.get(key))); 108 } 109 String queryString = URLEncodedUtils.format(params,ENCODING); 110 if (url.indexOf("?") > -1) {//存在?,表示这时的URL已经带参数了 111 url += "&" + queryString; 112 } else { 113 url += "?" + queryString; 114 } 115 } 116 117 HttpGet httpGet = new HttpGet(url); 118 119 /* 120 * 设置头信息 121 */ 122 if (MapUtils.isNotEmpty(headerMap)) { 123 Set<String> keySet = headerMap.keySet(); 124 for (String key : keySet) { 125 httpGet.addHeader(key, headerMap.get(key)); 126 } 127 } 128 129 String result = ""; 130 131 HttpResponse response = client.execute(httpGet); //发出get请求 132 StatusLine status = response.getStatusLine(); //获取返回的状态码 133 HttpEntity entity = response.getEntity(); //获取返回的响应内容 134 if (status.getStatusCode() == HttpStatus.SC_OK) { //200 135 result = EntityUtils.toString(entity, ENCODING); 136 } 137 138 httpGet.abort();//中止请求,连接被释放回连接池 139 return result; 140 } 141 142 /** 143 * post请求 144 * @param url //请求URL 145 * @param paramMap //请求参数 146 * @param headerMap //请求头信息 147 */ 148 public static String post(String url, 149 Map<String, String> paramMap, 150 Map<String, String> headerMap) throws ClientProtocolException, 151 IOException { 152 HttpPost httpPost = new HttpPost(url); 153 /* 154 * 处理参数 155 */ 156 List<NameValuePair> params = new ArrayList<NameValuePair>(); 157 if (MapUtils.isNotEmpty(paramMap)) { 158 Set<String> keySet = paramMap.keySet(); 159 for (String key : keySet) { 160 params.add(new BasicNameValuePair(key, paramMap.get(key))); 161 } 162 } 163 164 /* 165 * 设置头信息 166 */ 167 if (MapUtils.isNotEmpty(headerMap)) { 168 Set<String> keySet = headerMap.keySet(); 169 for (String key : keySet) { 170 httpPost.addHeader(key, headerMap.get(key)); 171 } 172 } 173 174 String result = ""; 175 176 httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));//设置参数 177 HttpResponse response = client.execute(httpPost); //发出post请求 178 StatusLine status = response.getStatusLine(); //获取返回的状态码 179 HttpEntity entity = response.getEntity(); //获取响应内容 180 if (status.getStatusCode() == HttpStatus.SC_OK) { 181 result = EntityUtils.toString(entity, ENCODING); 182 } 183 184 httpPost.abort();//中止请求,连接被释放回连接池 185 return result; 186 } 187 188 /** 189 * 测试 190 */ 191 public static void main(String[] args) { 192 try { 193 System.out.println(HttpClientUtil.get("https://www.baidu.com/", null, null)); 194 //System.out.println(HttpClientUtil.post("http://www.cppblog.com/iuranus/archive/2010/07/04/119311.html", null, null)); 195 } catch (ClientProtocolException e) { 196 e.printStackTrace(); 197 } catch (IOException e) { 198 e.printStackTrace(); 199 } 200 } 201 }
在该代码中,还有两个部分:一个属性文件http.properties和一个文件操作类FileUtil。这两部分,请查看下边的这个链接:
http://www.cnblogs.com/java-zhao/p/5098813.html
注意:
- 我们发起的请求可以使http的,也可以是https(相当于http+SSL/TLS+数字证书的组合,是一种安全协议)的,对于https相关的请求而言,我们需要编写一些代码,来做特殊的处理。一般而言,处理https请求有两种方法:
1)将https服务器端的安全证书导入到客户端的TrustStore文件中去,具体的原理见"《微信公众平台应用开发(方法、技巧与案例)》第5章"或者去查看柳峰的博客
2)实现自定义的信任管理器(eg.MyX509TrustManager),需要实现X509TrustManager接口,并实现其中的三个方法。注意:这个类的注释一定要看
3)注意http大多数时候用的是8080端口而不是80
第一种方法需要手工导入证书,很费事;第二种方法十分灵活。
- 对于HttpClientUtil中,每一块做什么查看注释,这里:解释httpclient.max.conn.per.route(每个路由的最大连接数):这里路由的概念可以理解为"运行环境机器到目标机器"的一条线路。举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route(路由),根据如上设置为20,就可以为上边两条route分别设置最大20个并发连接数。
- 假如只有HttpClientUtil使用MyX509TrustManager,我们也可以将MyX509TrustManager作为HttpClientUtil的一个内部类,代码如下:
-
1 package com.util; 2 3 import java.io.IOException; 4 import java.security.KeyManagementException; 5 import java.security.NoSuchAlgorithmException; 6 import java.security.NoSuchProviderException; 7 import java.security.SecureRandom; 8 import java.security.cert.CertificateException; 9 import java.security.cert.X509Certificate; 10 import java.util.ArrayList; 11 import java.util.List; 12 import java.util.Map; 13 import java.util.Properties; 14 import java.util.Set; 15 16 import javax.net.ssl.SSLContext; 17 import javax.net.ssl.TrustManager; 18 import javax.net.ssl.X509TrustManager; 19 20 import org.apache.commons.collections4.MapUtils; 21 import org.apache.http.HttpEntity; 22 import org.apache.http.HttpResponse; 23 import org.apache.http.HttpStatus; 24 import org.apache.http.HttpVersion; 25 import org.apache.http.NameValuePair; 26 import org.apache.http.StatusLine; 27 import org.apache.http.client.ClientProtocolException; 28 import org.apache.http.client.HttpClient; 29 import org.apache.http.client.entity.UrlEncodedFormEntity; 30 import org.apache.http.client.methods.HttpGet; 31 import org.apache.http.client.methods.HttpPost; 32 import org.apache.http.client.utils.URLEncodedUtils; 33 import org.apache.http.conn.scheme.PlainSocketFactory; 34 import org.apache.http.conn.scheme.Scheme; 35 import org.apache.http.conn.scheme.SchemeRegistry; 36 import org.apache.http.conn.ssl.SSLSocketFactory; 37 import org.apache.http.impl.client.DefaultHttpClient; 38 import org.apache.http.impl.conn.PoolingClientConnectionManager; 39 import org.apache.http.message.BasicNameValuePair; 40 import org.apache.http.params.BasicHttpParams; 41 import org.apache.http.params.CoreConnectionPNames; 42 import org.apache.http.params.CoreProtocolPNames; 43 import org.apache.http.params.HttpParams; 44 import org.apache.http.util.EntityUtils; 45 46 /** 47 * 对HTTPClient的封装 48 */ 49 public class HttpClientUtilWithMyX509TrustMananer { 50 51 private static final String ENCODING = "UTF-8"; 52 53 private static HttpClient client = null; 54 private static SchemeRegistry schemeRegistry; // 协议控制 55 private static PoolingClientConnectionManager ccm; // HttpClient连接池(多连接的线程安全的管理器) 56 57 static { 58 try { 59 /* 60 * 与https请求相关的操作 61 */ 62 SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); 63 sslContext.init(null, 64 new TrustManager[] { getMyX509TrustManager() }, 65 new SecureRandom()); 66 SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext); 67 /* 68 * 定义访问协议 69 */ 70 schemeRegistry = new SchemeRegistry(); 71 schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));// http 72 schemeRegistry.register(new Scheme("https", 443, socketFactory));// https 73 } catch (NoSuchAlgorithmException e) { 74 e.printStackTrace(); 75 } catch (NoSuchProviderException e) { 76 e.printStackTrace(); 77 } catch (KeyManagementException e) { 78 e.printStackTrace(); 79 } 80 81 Properties props = FileUtil.loadProps("http.properties");// 加载属性文件 82 83 // 连接池管理 84 ccm = new PoolingClientConnectionManager(schemeRegistry); 85 ccm.setDefaultMaxPerRoute(FileUtil.getInt(props,"httpclient.max.conn.per.route", 20));// 每个路由的最大连接数 86 ccm.setMaxTotal(FileUtil.getInt(props, "httpclient.max.conn.total", 400));// 最大总连接数 87 88 HttpParams httpParams = new BasicHttpParams(); 89 httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,FileUtil.getInt(props, "httpclient.max.conn.timeout", 1000));// 连接超时时间(ms) 90 httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT,FileUtil.getInt(props, "httpclient.max.socket.timeout", 2000));// 操作超时时间(ms) 91 httpParams.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,HttpVersion.HTTP_1_1);// 设置http1.1或http1.0 92 93 client = new DefaultHttpClient(ccm, httpParams);// 一个客户端就有一个连接池 94 } 95 96 /** 97 * get请求 98 * @param url 请求URL 99 * @param paramMap 请求参数 100 * @param headerMap 请求头信息 101 */ 102 public static String get(String url, 103 Map<String, String> paramMap, 104 Map<String, String> headerMap) throws ClientProtocolException, 105 IOException { 106 /* 107 * 拼接URL与参数 108 */ 109 if (MapUtils.isNotEmpty(paramMap)) { 110 List<NameValuePair> params = new ArrayList<NameValuePair>(); 111 for (String key : paramMap.keySet()) { 112 params.add(new BasicNameValuePair(key, paramMap.get(key))); 113 } 114 String queryString = URLEncodedUtils.format(params, ENCODING); 115 if (url.indexOf("?") > -1) {// 存在?,表示这时的URL已经带参数了 116 url += "&" + queryString; 117 } else { 118 url += "?" + queryString; 119 } 120 } 121 122 HttpGet httpGet = new HttpGet(url); 123 124 /* 125 * 设置头信息 126 */ 127 if (MapUtils.isNotEmpty(headerMap)) { 128 Set<String> keySet = headerMap.keySet(); 129 for (String key : keySet) { 130 httpGet.addHeader(key, headerMap.get(key)); 131 } 132 } 133 134 String result = ""; 135 136 HttpResponse response = client.execute(httpGet); // 发出get请求 137 StatusLine status = response.getStatusLine(); // 获取返回的状态码 138 HttpEntity entity = response.getEntity(); // 获取返回的响应内容 139 if (status.getStatusCode() == HttpStatus.SC_OK) { // 200 140 result = EntityUtils.toString(entity, ENCODING); 141 } 142 143 httpGet.abort();// 中止请求,连接被释放回连接池 144 return result; 145 } 146 147 /** 148 * post请求 149 * @param url 请求URL 150 * @param paramMap 请求参数 151 * @param headerMap 请求头信息 152 */ 153 public static String post(String url, 154 Map<String, String> paramMap, 155 Map<String, String> headerMap) throws ClientProtocolException, 156 IOException { 157 HttpPost httpPost = new HttpPost(url); 158 /* 159 * 处理参数 160 */ 161 List<NameValuePair> params = new ArrayList<NameValuePair>(); 162 if (MapUtils.isNotEmpty(paramMap)) { 163 Set<String> keySet = paramMap.keySet(); 164 for (String key : keySet) { 165 params.add(new BasicNameValuePair(key, paramMap.get(key))); 166 } 167 } 168 169 /* 170 * 设置头信息 171 */ 172 if (MapUtils.isNotEmpty(headerMap)) { 173 Set<String> keySet = headerMap.keySet(); 174 for (String key : keySet) { 175 httpPost.addHeader(key, headerMap.get(key)); 176 } 177 } 178 179 String result = ""; 180 181 httpPost.setEntity(new UrlEncodedFormEntity(params, ENCODING));// 设置参数 182 HttpResponse response = client.execute(httpPost); // 发出post请求 183 StatusLine status = response.getStatusLine(); // 获取返回的状态码 184 HttpEntity entity = response.getEntity(); // 获取响应内容 185 if (status.getStatusCode() == HttpStatus.SC_OK) { 186 result = EntityUtils.toString(entity, ENCODING); 187 } 188 189 httpPost.abort();// 中止请求,连接被释放回连接池 190 return result; 191 } 192 193 /** 194 * 构建自定义信任管理器内部类 195 */ 196 private static class MyX509TrustManager implements X509TrustManager { 197 /** 198 * 检查客户端证书,若不信任,抛出异常 199 */ 200 public void checkClientTrusted(X509Certificate[] arg0, String arg1) 201 throws CertificateException { 202 } 203 /** 204 * 检查服务端证书,若不信任,抛出异常,反之,若不抛出异常,则表示信任(所以,空方法代表信任所有的服务端证书) 205 */ 206 public void checkServerTrusted(X509Certificate[] arg0, String arg1) 207 throws CertificateException { 208 } 209 /** 210 * 返回受信任的X509证书数组 211 */ 212 public X509Certificate[] getAcceptedIssuers() { 213 return null; 214 } 215 } 216 217 /** 218 * 为外部类获取内部类提供方法 219 */ 220 public static MyX509TrustManager getMyX509TrustManager() { 221 return new MyX509TrustManager(); 222 } 223 224 /** 225 * 测试 226 */ 227 public static void main(String[] args) { 228 try { 229 System.out.println(HttpClientUtilWithMyX509TrustMananer.get("https://www.baidu.com/", null, null)); 230 // System.out.println(HttpClientUtil.post("http://www.cppblog.com/iuranus/archive/2010/07/04/119311.html", null, null)); 231 } catch (ClientProtocolException e) { 232 e.printStackTrace(); 233 } catch (IOException e) { 234 e.printStackTrace(); 235 } 236 } 237 }
注:在这里我定义了一个成员内部类,并提供了一个获取成员内部类的方法getMyX509TrustManager(),用于外部类来获取该内部类的实例。当然,如果对于内部类不熟的话,可以不使用内部类,直接使用上边的方式也好。