如何封装 httpclient封装类

博客分类:
1 httpClient封装后使用,get和post方法
import java.io.BufferedR
import java.io.InputS
import java.io.InputStreamR
import java.util.ArrayL
import java.util.I
import java.util.L
import java.util.M
import net.sf.json.JSONO
import org.apache.http.HttpE
import org.apache.http.HttpR
import org.apache.http.NameValueP
import org.apache.http.client.HttpC
import org.apache.http.client.entity.UrlEncodedFormE
import org.apache.http.client.methods.HttpP
import org.apache.http.entity.StringE
import org.apache.http.impl.client.DefaultHttpC
import org.apache.http.message.BasicNameValueP
import org.apache.http.protocol.HTTP;
* @Title:TestHttpClient
* @Author Tony
* @Date: 日 下午3:29:37
* @Description:
httpClient使用,1 发送post请求
2 发送get请求
public class TestHttpClient {
* @Title: methodPost
* @Description: httpclient方法中post提交数据的使用
* @param @return
* @param @throws Exception
* @return String
public static String methodPost() throws Exception {
DefaultHttpClient httpclient = new DefaultHttpClient();
// // 代理的设置
// HttpHost proxy = new HttpHost("10.60.8.20", 8080);
// httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
// proxy);
// 目标地址
HttpPost httppost = new HttpPost(
"http://localhost:8011/testServlet");
System.out.println("请求: " + httppost.getRequestLine());
// post 参数 传递
List&BasicNameValuePair& nvps = new ArrayList&BasicNameValuePair&();
nvps.add(new BasicNameValuePair("content", "")); // 参数
nvps.add(new BasicNameValuePair("path", "D:/file")); // 参数
nvps.add(new BasicNameValuePair("name", "8")); // 参数
nvps.add(new BasicNameValuePair("age", "9")); // 参数
nvps.add(new BasicNameValuePair("username", "wzt")); // 参数
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); // 设置参数给Post
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: "
+ entity.getContentLength());
// 显示结果
BufferedReader reader = new BufferedReader(new InputStreamReader(
entity.getContent(), "UTF-8"));
String line =
while ((line = reader.readLine()) != null) {
System.out.println(line);
if (entity != null) {
entity.consumeContent();
* @Title: methodGet
* @Description:
以get方法提交数的使用
* @param @return
* @param @throws Exception
* @return String
public static
String methodGet() throws
Exception {
DefaultHttpClient httpclient = new DefaultHttpClient();
// 代理的设置
HttpHost proxy = new HttpHost("10.60.8.20", 8080);
httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
// 目标地址
HttpPost httpGet = new HttpPost("http://localhost:8011/testServlet");
// 构造最简单的字符串数据
StringEntity reqEntity = new StringEntity("name=test&password=test");
// 设置类型
reqEntity.setContentType("application/x-www-form-urlencoded");
// 设置请求的数据
httpGet.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httpGet);
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
//得到返回数据的长度
// 显示结果
BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));
String line =
while ((line = reader.readLine()) != null) {
System.out.println(line);
if (entity != null) {
entity.consumeContent();
* 模拟url访问 从特定的url中获取json
* @param urlStr
* @param params
* @return json object ,or null if failed
* 参数经过封装后传过来 ,提交为 post请求
private static JSONObject getJsonFromUrl(String urlStr,
Map&String, String& params) {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(urlStr);
JSONObject json =
if (params != null) {
Iterator&String& keys = params.keySet().iterator();
List&NameValuePair& nvps = new ArrayList&NameValuePair&();
while (keys.hasNext()) {
String key = keys.next();
nvps.add(new BasicNameValuePair(key, params.get(key)));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
byte[] bytes = new byte[256];
StringBuffer sb = new StringBuffer();
while (is.read(bytes) & 0) {
sb.append(new String(bytes));
bytes = new byte[256];
json = JSONObject.fromObject(sb.toString());
} catch (Exception e) {
e.printStackTrace();
* @Title: main
* @Description: 测试类
* @param @param args
* @return void
public static void main(String[] args) {
TestHttpClient.methodGet();
TestHttpClient.methodPost();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
2 后台接受数据的servlet
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding( "utf-8");
response.setContentType("text/charset=utf-8");
response.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
System.out.println("name 变量数据: "+name);
(873.8 KB)
下载次数: 1039
(142.3 KB)
下载次数: 166
浏览 59589
HttpHost proxy = new HttpHost("ip",端口);&
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy);对我有用再次对我有用
只有个测试方法,你能把客户端& 服务端代码给我贴全吗?今天刚看到问题,又重新整理了一下整个方法,加入了一个jar文件,服务端就是一个Servlet就可以了。
浏览: 345803 次
来自: 黑龙江
lizhenlzlz 写道HttpHost proxy = n ...
HttpHost proxy = new HttpHost(& ...
methodGet()方法里
HttpPost httpGet ...
import org.apache.http.impl.cli ...
import org.apache.h没啊。这个包
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'转载请注明出处!谢谢
httpClient介绍
是一个实现了协议的开源客户端工具库,可以通过程序发送请求。
HttpClient发送请求和接收响应
以请求为例,以下代码获得主页内容并将返回结果打印出来。
HttpGet httpget = new HttpGet("/");
System.out.println("executing request " + httpget.getURI());
// 创建处理器
ResponseHandler&String& responseHandler = new BasicResponseHandler();
String responseBody = httpclient.execute(httpget, responseHandler);
System.out.println("----------------------------------------");
System.out.println(responseBody);
System.out.println("----------------------------------------");
} finally {
//HttpClient不再使用时,关闭连接管理器以保证所有资源的释放
httpclient.getConnectionManager().shutdown();
执行一次请求,即运行一次方法,时序图如下:
时序图说明
时序图编号说明
1.1、1.2、1.3等均为操作1的子操作,即:操作1 execute()中又分别调用了操作1.1 createClientConnectionManager()、操作1.2 createClientRequestDirector()以及操作1.3 requestDirector 对象的execute()方法等,以此类推。
按时间先后顺序分别编号为1,2,3等,以此类推。
主要类说明
对于图中各对象,httpClient jar包中均提供对应的接口及相应的实现类。
图中直接与服务器进行socket通信的是最右端接口OperatedClientConnection某一实现类的对象,图中从右到左进行了层层的封装,最终开发人员直接使用的是接口HttpClient某一实现类的对象进行请求的发送和响应的接收(如2.1.1代码示例)。
时序图中各对象所在类关系如下图类图所示(仅列出图中所出现的各个类及方法,参数多的方法省略部分参数,其他类属性和操作请参照源码):
1.1.3.2.1.
接口OperatedClientConnection
该接口对应一个http连接,与服务器端建立socket连接进行通信。
1.1.3.2.2.
接口ManagedClientConnection
该接口对一个http连接OperatedClientConnection进行封装,ManagedClientConnection维持一个PoolEntry&HttpRoute, OperatedClientConnection&路由和连接的对应。提供方法获得对应连接管理器,对http连接的各类方法,如建立连接,获得相应,关闭连接等进行封装。
1.1.3.2.3.
接口RequestDirector
RequestDirector为消息的发送执行者,该接口负责消息路由的选择和可能的重定向,消息的鉴权,连接的分配回收(调用ClientConnectionManager相关方法),建立,关闭等并控制连接的保持。
连接是否保持以及保持时间默认原则如下:
连接是否保持:客户端如果希望保持长连接,应该在发起请求时告诉服务器希望服务器保持长连接(http 1.0设置connection字段为keep-alive,http 1.1字段默认保持)。根据服务器的响应来确定是否保持长连接,判断原则如下:
检查返回response报文头的Transfer-Encoding字段,若该字段值存在且不为chunked,则连接不保持,直接关闭。其他情况进入下一步。
检查返回的response报文头的Content-Length字段,若该字段值为空或者格式不正确(多个长度,值不是整数),则连接不保持,直接关闭。其他情况进入下一步
检查返回的response报文头的connection字段(若该字段不存在,则为Proxy-Connection字段)值
如果这俩字段都不存在,则http 1.1版本默认为保持,将连接标记为保持, 1.0版本默认为连接不保持,直接关闭。
如果字段存在,若字段值为close 则连接不保持,直接关闭;若字段值为keep-alive则连接标记为保持。
连接保持时间:连接交换至连接管理时,若连接标记为保持,则将由连接管理器保持一段时间;若连接没有标记为保持,则直接从连接池中删除并关闭entry。连接保持时,保持时间规则如下:
保持时间计时开始时间为连接交换至连接池的时间。
保持时长计算规则为:获取keep-alive字段中timeout属性的值,
若该字段存在,则保持时间为 timeout属性值*1000,单位毫秒。
若该字段不存在,则连接保持时间设置为-1,表示为无穷。
响应头日志示例:
若需要修改连接的保持及重用默认原则,则需编写子类继承自AbstractHttpClient,分别覆盖其
createConnectionReuseStrategy() 和createConnectionKeepAliveStrategy() 方法。
1.1.3.2.4.
接口ClientConnectionManager
ClientConnectionManager为连接池管理器,是线程安全的。Jar包中提供的具体实现类有BasicClientConnectionManager和PoolingClientConnectionManager。其中BasicClientConnectionManager只管理一个连接。PoolingClientConnectionManager管理连接池。
若有特殊需要,开发人员可自行编写连接管理器实现该接口。
连接管理器自动管理连接的分配以及回收工作,并支持连接保持以及重用。连接保持以及重用由RequestDirector进行控制。
1.1.3.2.5.
接口HttpClient
接口HttpClient为开发人员直接使用的发送请求和接收响应的接口,是线程安全的。jar包中提供的实现类有:AbstractHttpClient, DefaultHttpClient, AutoRetryHttpClient, ContentEncodingHttpClient, DecompressingHttpClient, SystemDefaultHttpClient。其中其他所有类都继承自抽象类AbStractHttpClient,该类使用了门面模式,对http协议的处理进行了默认的封装,包括默认连接管理器,默认消息头,默认消息发送等,开发人员可以覆盖其中的方法更改其默认设置。
AbstractHttpClient默认设置连接管理器为BasicClientConnectionManager。若要修改连接管理器,则应该采用以下方式之一:
初始化时,传入连接池,例如:
= new PoolingClientConnectionManager();
编写httpClient接口的实现类,继承自AbstractHttpClient并覆盖其createClientConnectionManager()方法,在方法中创建自己的连接管理器。
createClientConnectionManager(),创建连接池,该方法为protected。子类可覆盖修改默认连接池。
createClientRequestDirector(),创建请求执行者,该方法为。子类可覆盖但一般不需要。
httpClient中调用1.2方法所创建的请求执行者requestDirector的execute()方法。该方法中依次调用如下方法:
1.3.1调用连接管理器的requestConnection(route, userToken)方法,该方法调用连接池httpConnPool的lease方法,创建一个Future&HttpPoolEntry&。Futrue用法参见Java标准API。返回clientConnectionRequest。
1.3.2.调用clientConnectionRequest的getConnection(timeout, TimeUnit.MILLISECONDS)方法,该方法负责将连接池中可用连接分配给当前请求,具体如下:
创建clientConnectionOperator。
执行1.3.1中创建的Future的任务,该任务获得当前可用的poolEntry&router,OperatedClientConnection&并封装成managedClientConnectionImpl返回。
1.3.3. 调用 tryConnect(roureq, context)方法,该方法最终调用OperatedClientConnection的openning方法,与服务器建立socket连接。
1.3.4. 调用 tryExecute(roureq, context)方法,该方法最终调用OperatedClientConnection的receiveResponseHeader()和receiveResponseEntity()获得服务器响应。
1.3.5 判断连接是否保持用来重用,若保持,则设置保持时间,并将连接标记为可重用不保持则调用managedClientConnectionImpl的close方法关闭连接,该方法最终调用OperatedClientConnection的close()方法关闭连接。
最终respose返回至httpClient。
发送请求的线程需处理当前连接,若已被标记为重用,则交还至连接池管理器;否则,关闭当前连接。(使用响应处理器ResponseHanler)。本次请求结束。
httpClient连接池
若连接管理器配置为,则将使用连接池来管理连接的分配,回收等操作。
连接池结构
连接池结构图如下,其中:
PoolEntry&HttpRoute, OperatedClientConnection&为路由和连接的对应。
routeToPool可以多个(图中仅示例两个);图中各队列大小动态变化,并不相等;
maxTotal限制的是外层httpConnPool中leased集合和available队列的总和的大小,leased和available的大小没有单独限制;
同理:maxPerRoute限制的是routeToPool中leased集合和available队列的总和的大小;
连接池工作原理
分配连接给当前请求包括两部分:从连接池获取可用连接;将连接与当前请求绑定。其中第一部分从连接池获取可用连接的过程为:
获取route对应连接池routeToPool中可用的连接,有则返回该连接。若没有则转入下一步。
若routeToPool和外层HttpConnPool连接池均还有可用的空间,则新建连接,并将该连接作为可用连接返回;否则进行下一步
将当前请求放入pending队列,等待执行。
上述过程中包含各个队列和集合的删除,添加等操作以及各种判断条件,具体流程如下:
连接用完之后连接池需要进行回收,具体流程如下:
若当前连接标记为重用,则将该连接从routeToPool中的leased集合删除,并添加至available队列,同样的将该请求从外层httpConnPool的leased集合删除,并添加至其available队列。同时唤醒该routeToPool的pending队列的第一个PoolEntryFuture。将其从pending队列删除,并将其从外层httpConnPool的pending队列中删除。
若连接没有标记为重用,则分别从routeToPool和外层httpConnPool中删除该连接,并关闭该连接。
过期和空闲连接的关闭
连接如果标记为保持时,将由连接管理器保持一段时间,此时连接可能出现的情况是:
连接处于空闲状态,时间已超过连接保持时间
连接处于空闲状态,时间没有超过连接保持时间
以上两种情况中,随时都会出现连接的服务端已关闭的情况,而此时连接的客户端并没有阻塞着去接受服务端的数据,所以客户端不知道连接已关闭,无法关闭自身的socket。
连接池提供的方法:
首先连接池在每个请求获取连接时,都会在RouteToPool的available队列获取Entry并检测此时Entry是否已关闭或者已过期,若是则关闭并移除该Entry。
closeExpiredConnections()该方法关闭超过连接保持时间的空闲连接。
closeIdleConnections(timeout,tunit)该方法关闭空闲时间超过timeout的连接,空闲时间从交还给连接管理器时开始,不管是否已过期超过空闲时间则关闭。所以Idle时间应该设置的尽量长一点。
以上两个方法连接关闭的过程均是:
RouteToPool中删除当前entry。先删available队列中的,如果没有,再删除leased集合中的。
httpConnPool中删除当前entry。删除过程同RouteToPool
唤醒阻塞在RouteToPool中的第一个future。
相关原理说明
Tcp连接的关闭
连接实际上在传输层建立的是连接,最终利用的是进行通信。连接的保持和关闭实际上都和连接的关闭有关。关闭过程如下图:
TCP连接程序中使用socket编程进行实现。一条TCP是一条抽象的连接通道,由通信双方的IP+端口号唯一确定,两端分别通过socket实例进行操作,一个socket实例包括一个输入通道和输出通道,一端的输出通道为另一端的输入通道。
Tcp连接的关闭是连接的两端分别都需要进行关闭(调用close(socket),该函数执行发送FIN,等待ACK等图示操作)。实际上没有客户端和服务端的区别,只有主动关闭和被动关闭的区别。对于上层的其http连接,实际上也就是http服务端主动关闭或者http客户端主动关闭,而不管谁主动,最终服务端和客户端都需要调用close(socket)关闭连接。
主动关闭的一端A调用了close函数之后,若另一端B并没有阻塞着等待着数据,就无法检测到连接的A端已关闭,就没法关闭自身的socket,造成资源的浪费。http连接都是一次请求和响应,之后便交回给连接管理池,因此在http连接池中应当能够移除已过期或者空闲太久的连接,因为他们可能已经被服务器端关闭或者客户端短期内不再使用。
TIME_WAIT状态:
可靠地实现TCP全双工连接的终止
在进行关闭连接四路握手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,被动关闭端将重发最终的FIN,因此主动关闭端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么主动关闭端将发送RST分节(复位),被动关闭端将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。因而,要实现TCP全双工连接的正常终止,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。
允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。
httpClient最佳实践
原:不再升级维护,使用Apache HttpComponents的HttpClient代替。Pom文件修改如下:
原maven依赖:
&groupId&commons-httpclient&/groupId&
&artifactId&commons-httpclient&/artifactId&
&version&3.1&/version&
&groupId&org.apache.httpcomponents&/groupId&
&artifactId&httpclient&/artifactId&
&version&4.2.1&/version&
使用连接池管理器
编写类继承自DefaultHttpClient(以下假设为SopHttpClient),覆盖其createClientConnectionManager()方法,方法中创建连接池管理器。
开启一个线程(假设为IdleConnectionMonitorThread)用来清除连接池中空闲和过期的连接。
Spring配置中使用默认scope,即单例模式,其他类使用时由Spring配置进行依赖注入,不要使用new方法。SopHttpClient应该提供方法destroy()并配置在Spring销毁该bean前调用,destory()方法中关闭对应连接池管理器和监控线程IdleConnectionMonitorThread。
异常处理机制(请求和响应):
编写类实现接口HttpRequestRetryHandler(可参照默认实现DefaultHttpRequestRetryHandler),并覆盖AbstractHttpClient中的createHttpRequestRetryHandler()方法创建新的重试处理机制。
参数可配置
各参数(连接池默认ip、端口和大小等,超时时间等)尽量都集中在SopHttpClient类中,设置为由Spring进行统一配置,且提供接口在程序中修改。
保证连接交回至连接池管理器
这两段代码返回的entity是HttpEntity的实现类BasicManagedEntity。此时与本次请求关联的连接尚未归还至连接管理器。需要调用以下两条语句:
获得响应具体内容
处理响应:代码省略
关闭输入流同时会将连接交回至连接处理器
使用默认的响应处理器BasicResponseHandler
httpClient Jar包中提供BasicResponseHandler。如果返回的类型能确定需要解码为String类型的话,推荐使用该响应处理器。
该处理器解码http连接响应字节流为String类型,对返回码&=300的响应进行了异常封装,并能够保证连接交还给连接池管理器。
该处理器将字节解码为字符的过程依次如下:
如果响应http报文Head部分由指定的charset,则使用该charset进行解码,否则进行下一步。例如使用UTF-8解码以下响应:
17:59:42.051 [main] DEBUG org.apache.http.headers - && Content-Type: text/ charset=utf-8
如果响应报文未执行charset,则使用传入EntityUntils.toString()时指定的charset进行解码。否则进行下一步
使用ISO-8859-1进行解码。
BasicManagedEntity关闭连接池管理器原理
BasicManagedEntity实现了三个接口:HttpEntity,ConnectionReleaseTrigger, EofSensorWatcher。
调用BasicManagedEntity的getContent方法时,实际上初始化了的实例,并将BasicManagedEntity当前对象自身作为传入。
类的继承体系,实现了接口
public InputStream getContent() throws IOException {
return new EofSensorInputStream(wrappedEntity.getContent(), this);
构造函数声明
调用的方法,该方法调用自身的方法,方法中调入了传入的的方法并关闭输入流,由于上一步骤中实际传入的是BasicManagedEntity的实例,因此实际上调用的是BasicManagedEntity的streamClose()方法。
public void close() throws IOException {
// tolerate multiple calls to close()
selfClosed =
checkClose();
if (wrappedStream != null) {
boolean scws = // should close wrapped stream?
if (eofWatcher != null)
scws = eofWatcher.streamClosed(wrappedStream);
wrappedStream.close();
} finally {
wrappedStream =
BasicManagedEntity的streamClose()方法中将连接交回至连接池管理器。
if (attemptReuse && (managedConn != null)) {
boolean valid = managedConn.isOpen();
// this assumes that closing the stream will
// consume the remainder of the response body:
wrapped.close();
managedConn.markReusable();
} catch (SocketException ex) {
if (valid) {
} finally {
releaseManagedConnection();
提供了非常灵活的架构,同时提供了很多接口,需要修改时,找到对应接口和默认实现类,参照默认实现类进行修改即可(或继承默认实现类,覆盖其对应方法)。通常需要更改的类有和各种以及
浏览 10850
浏览: 21169 次
来自: 北京
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'& &&&使用HttpClient进行网络连接时,参数的封装式如何进行的?
使用HttpClient进行网络连接时,参数的封装式如何进行的?
最近自己预习HttpClient网络访问,但是数据的封装一块查阅了资料很麻烦,有没有了解过的呢?
悬赏2青鸟豆&|&浏览343
才能回答问题
HttpClient通过Post方式传递数据时,通过(1) post.setEntity(HttpEntity)设置数据,但是HttpEntity是接口,需要使用其实现类UrlEncodedFormEntity,所以进而修改为:(2)post.setEntity(new UrlEncodedFormEntity(List&? extends NameValuesPair&)),可见需要传入一个List集合,而泛型为NameValuesPair的子类,但是NameValuesPair也是接口,所以继续使用其实现类BasicNameValuePair,进而修改为:(3)post.setEntity(new UrlEncodedFormEntity(new BasicNameValuePair(&&,&&))).此时数据已经封装好。
本周答题榜
本周回答64题
本周回答23题
本周回答6题
本周回答4题
本周回答3题
本周回答3题
本周回答3题
本周回答2题
Beijing Aptech Beida Jade Bird Information Technology Co.,Ltd北大青鸟IT教育 北京阿博泰克北大青鸟信息技术有限公司 版权所有
京ICP备号-3

我要回帖

更多关于 .net httpclient 封装 的文章

 

随机推荐