最近公司android项目处于维护阶段,开始做了一些java和运维的项目,也用nexus搭建公司的私服,好了,废话也不多说,开始今天的主题,怎么用HttpClient的请求框架绑定本地发送请求端口
我们都知道,网路简历连接,都离不开socket套接字,所以只有在这里我们才能设置好网路请求一些参数
写了一个小程序,试了一下绑定socket的本地端口
public void send(String ip,int port,String url,String sendData)
{
try {
Socket socket = new Socket(ip, port,InetAddress.getByName("192.168.100.165"),10011);
OutputStreamWriter osw = new OutputStreamWriter(socket.getOutputStream());
StringBuffer sb = new StringBuffer();
//http协议中请求行
sb.append("GET "+url+" HTTP/1.1\r\n");
//http协议中请求头
sb.append("Host: "+ip+"\r\n");
sb.append("Connection: Keep-Alive\r\n");
sb.append("user-agent: 1601\r\n");
sb.append("\r\n");
osw.write(sb.toString());
osw.flush();
//传回的消息的头信息
InputStream is = socket.getInputStream();
String line = null;
int contentLength = 0;
// 服务器发送过来的请求参数头部信息
do {
line = readLine(is, 0);
//如果有Content-Length消息头时取出
if (line.startsWith("Content-Length")) {
contentLength = Integer.parseInt(line.split(":")[1].trim());
}
//打印请求部信息
System.out.print(line);
//单独的回车换行,则表示请求头结束
} while (!line.equals("\r\n"));
System.out.print(readLine(is, contentLength));
is.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String readLine(InputStream is, int contentLe) throws IOException {
ArrayList lineByteList = new ArrayList();
byte readByte;
int total = 0;
if (contentLe != 0) {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
total++;
} while (total < contentLe);//消息体读还未读完
} else {
do {
readByte = (byte) is.read();
lineByteList.add(Byte.valueOf(readByte));
} while (readByte != 10);
}
byte[] tmpByteArr = new byte[lineByteList.size()];
for (int i = 0; i < lineByteList.size(); i++) {
tmpByteArr[i] = ((Byte) lineByteList.get(i)).byteValue();
}
lineByteList.clear();
return new String(tmpByteArr, encoding);
}
public static void main(String[] args) {
SimpleHttpClient httpClient=new SimpleHttpClient();
httpClient.send("61.135.169.121",
443, "/index.php?tn=monline_3_dg",
null);
}
请求的是百度的地址,我们抓包看看,端口是真的绑定了
这里可以看到,发送的资源端口为10011,与上面绑定的恩地端口10011是一样的,为了防止是巧合,我们再请求两次
可以看到,端口确实是绑定了。
接下来,就是正式用到项目里面了
首先,我们来看一下公司直接的rest请求工具类RestTemplateUtil
@Component
public class RestTemplateUtil {
private RestTemplate restTemplate;
private RestTemplate httpsRestTemplate;
private int timeout = 5000;
private String APPLICATION_JSON_UTF8 = "application/json;charset=UTF-8";
private String APPLICATION_XML_UTF8 = "application/xml;charset=UTF-8";
public RestTemplateUtil() {
restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(timeout);
requestFactory.setConnectTimeout(timeout);
restTemplate.setRequestFactory(requestFactory);
httpsRestTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactoryHttps = new HttpsClientRequestFactory();
requestFactoryHttps.setReadTimeout(timeout);
requestFactoryHttps.setConnectTimeout(timeout);
httpsRestTemplate.setRequestFactory(requestFactoryHttps);
}
private <T> T post(String url, String param, String mediaType, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(mediaType);
headers.setContentType(type);
return exchange(url, param, HttpMethod.POST, bodyType, headers);
}
public <T> T postJson(String url, String param, Class<T> bodyType) {
return post(url, param, APPLICATION_JSON_UTF8, bodyType);
}
public <T> T postXml(String url, String param, Class<T> bodyType) {
return post(url, param, APPLICATION_XML_UTF8, bodyType);
}
public <T> T get(String url, String param, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(APPLICATION_JSON_UTF8);
headers.setContentType(type);
return exchange(url, param, HttpMethod.GET, bodyType, headers);
}
public <T> T put(String url, String data, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(APPLICATION_JSON_UTF8);
headers.setContentType(type);
return exchange(url, data, HttpMethod.PUT, bodyType, headers);
}
public <T> T delete(String url, String data, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(APPLICATION_JSON_UTF8);
headers.setContentType(type);
return exchange(url, data, HttpMethod.DELETE, bodyType, headers);
}
/**
* 发送/获取 服务端数据(主要用于解决发送put,delete方法无返回值问题)
*
* @param url
* @param content
* @param method
* @param bodyType
* @param headers
* @param <T>
* @return
*/
private <T> T exchange(String url, String content, HttpMethod method, Class<T> bodyType, HttpHeaders headers) {
// 发送请求
HttpEntity<String> entity = new HttpEntity<>(content, headers);
ResponseEntity<T> resultEntity = restTemplate.exchange(url, method, entity, bodyType);
return resultEntity.getBody();
}
public <T> T convertToBean(Class<T> clz, String msg) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(clz);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
T t = (T) unmarshaller.unmarshal(new StringReader(msg));
return t;
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
public String convertToXml(Object bean) {
try {
JAXBContext context = JAXBContext.newInstance(bean.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(bean, writer);
return writer.toString();
} catch (JAXBException e) {
e.printStackTrace();
}
return null;
}
public <T> T httpsGet(String url, String param, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(APPLICATION_JSON_UTF8);
headers.setContentType(type);
return exchangeHttps(url, param, HttpMethod.GET, bodyType, headers);
}
public <T> T httpsPost(String url, String param, Class<T> bodyType) {
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(APPLICATION_JSON_UTF8);
headers.setContentType(type);
return exchangeHttps(url,param,HttpMethod.POST,bodyType,headers);
}
private <T> T exchangeHttps(String url, String content, HttpMethod method, Class<T> bodyType, HttpHeaders headers) {
// 发送请求
HttpEntity<String> entity = new HttpEntity<>(content, headers);
ResponseEntity<T> resultEntity = httpsRestTemplate.exchange(url, method, entity, bodyType);
return resultEntity.getBody();
}
}
可以看到,主要请求的配置还是在构造方法这里
public RestTemplateUtil() {
restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(timeout);
requestFactory.setConnectTimeout(timeout);
restTemplate.setRequestFactory(requestFactory);
httpsRestTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactoryHttps = new HttpsClientRequestFactory();
requestFactoryHttps.setReadTimeout(timeout);
requestFactoryHttps.setConnectTimeout(timeout);
httpsRestTemplate.setRequestFactory(requestFactoryHttps);
}
这里新建了两个ResTemplate对象,一个是用于http请求,一个则是用于https请求
主要注意这两个地方,一个是http请求的请求工厂类SimpleClientHttpRequestFactory,还有一个则是https请求工厂类HttpsClientRequestFactory,可以看出来https请求工厂类是继承SimpleClientHttpRequestFactory的,这里spring框架中的restemplate类,在初始化的时候都需要传入一个请求工厂来管理请求,那我们要控制端口,就只能从这个请求工厂类着手了
看一下传入的工厂是个什么东西
/**
* Set the request factory that this accessor uses for obtaining client request handles.
* <p>The default is a {@link SimpleClientHttpRequestFactory} based on the JDK's own
* HTTP libraries ({@link java.net.HttpURLConnection}).
* <p><b>Note that the standard JDK HTTP library does not support the HTTP PATCH method.
* Configure the Apache HttpComponents or OkHttp request factory to enable PATCH.</b>
* @see #createRequest(URI, HttpMethod)
* @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
* @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
*/
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
this.requestFactory = requestFactory;
}
点进方法,可以打看到传入的ClientHttpRequestFactory一个类,这是个接口,再看一下他的实现
可以看到,所有的实现类都必须实现
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
这个接口,用来创建请求
在实现类里面:
SimpleClientHttpRequestFactory,请求是用是的java自带的网络请求框架,而且它也只有一个集成类,就是那个https请求的工厂类HttpsClientRequestFactory
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
prepareConnection(connection, httpMethod.name());
if (this.bufferRequestBody) {
return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
}
else {
return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
}
}
我们看这个方法里面,创建的HttpURConnection对象,就是java.net包下的,好了,这个不是重点
里面我们还可以看到有okhttp和okhttp3的工厂类,这些都是用okhttp的网络请求框架来来实现的rest请求,因为没有导okhttp的包,所以源码进去会报错,时间原因,我也没有深究这个了
接下来,重点来了
HttpComponentsClientHttpRequestFactory请求工厂,使用的是HttpClient网络请求框架
注意这需要自己添加HttpClient相关的依赖包,我用的是这个
compile group: 'org.apache.httpcomponents', name: 'httpcore', version: '4.4.1'
compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.1'
看看这个类
/**
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with a default {@link HttpClient}.
*/
public HttpComponentsClientHttpRequestFactory() {
this.httpClient = HttpClients.createSystem();
}
/**
* Create a new instance of the {@code HttpComponentsClientHttpRequestFactory}
* with the given {@link HttpClient} instance.
* @param httpClient the HttpClient instance to use for this request factory
*/
public HttpComponentsClientHttpRequestFactory(HttpClient httpClient) {
setHttpClient(httpClient);
}
可以看到构造函数有两个,一个无参构造,另一个是有参,可以传入一个HttpClient的对象,既然要自定义,那肯定是选择第二个构造来创建这个对象咯
接下来就是这么写这个HttpClient实例了
public interface HttpClient {
看,这是个接口,看看无参是这么创建的实例
this.httpClient = HttpClients.createSystem();
用的是一个HttpCliens类来创建的实例对象,再去看看HttpClients
@Immutable
public class HttpClients {
private HttpClients() {
super();
}
/**
* Creates builder object for construction of custom
* {@link CloseableHttpClient} instances.
*/
public static HttpClientBuilder custom() {
return HttpClientBuilder.create();
}
/**
* Creates {@link CloseableHttpClient} instance with default
* configuration.
*/
public static CloseableHttpClient createDefault() {
return HttpClientBuilder.create().build();
}
/**
* Creates {@link CloseableHttpClient} instance with default
* configuration based on ssytem properties.
*/
public static CloseableHttpClient createSystem() {
return HttpClientBuilder.create().useSystemProperties().build();
}
/**
* Creates {@link CloseableHttpClient} instance that implements
* the most basic HTTP protocol support.
*/
public static CloseableHttpClient createMinimal() {
return new MinimalHttpClient(new PoolingHttpClientConnectionManager());
}
/**
* Creates {@link CloseableHttpClient} instance that implements
* the most basic HTTP protocol support.
*/
public static CloseableHttpClient createMinimal(final HttpClientConnectionManager connManager) {
return new MinimalHttpClient(connManager);
}
}
可以看到,调用的是HttpClientBuilder的类创建实例,useSystemProperties()方法只是设置一个配置参数
/**
* Use system properties when creating and configuring default
* implementations.
*/
public final HttpClientBuilder useSystemProperties() {
this.systemProperties = true;
return this;
}
是否为系统配置参数,在创建实例的时候会用到,主要是build()方法,我们进去看看
public CloseableHttpClient build() {
// Create main request executor
// We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version
PublicSuffixMatcher publicSuffixMatcherCopy = this.publicSuffixMatcher;
if (publicSuffixMatcherCopy == null) {
publicSuffixMatcherCopy = PublicSuffixMatcherLoader.getDefault();
}
HttpRequestExecutor requestExecCopy = this.requestExec;
if (requestExecCopy == null) {
requestExecCopy = new HttpRequestExecutor();
}
HttpClientConnectionManager connManagerCopy = this.connManager;
if (connManagerCopy == null) {
LayeredConnectionSocketFactory sslSocketFactoryCopy = this.sslSocketFactory;
if (sslSocketFactoryCopy == null) {
final String[] supportedProtocols = systemProperties ? split(
System.getProperty("https.protocols")) : null;
final String[] supportedCipherSuites = systemProperties ? split(
System.getProperty("https.cipherSuites")) : null;
HostnameVerifier hostnameVerifierCopy = this.hostnameVerifier;
if (hostnameVerifierCopy == null) {
hostnameVerifierCopy = new DefaultHostnameVerifier(publicSuffixMatcherCopy);
}
if (sslContext != null) {
sslSocketFactoryCopy = new SSLConnectionSocketFactory(
sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifierCopy);
} else {
if (systemProperties) {
sslSocketFactoryCopy = new SSLConnectionSocketFactory(
(SSLSocketFactory) SSLSocketFactory.getDefault(),
supportedProtocols, supportedCipherSuites, hostnameVerifierCopy);
} else {
sslSocketFactoryCopy = new SSLConnectionSocketFactory(
SSLContexts.createDefault(),
hostnameVerifierCopy);
}
}
}
@SuppressWarnings("resource")
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactoryCopy)
.build(),
null,
null,
null,
connTimeToLive,
connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
if (defaultConnectionConfig != null) {
poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
}
if (systemProperties) {
String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
s = System.getProperty("http.maxConnections", "5");
final int max = Integer.parseInt(s);
poolingmgr.setDefaultMaxPerRoute(max);
poolingmgr.setMaxTotal(2 * max);
}
}
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}
if (maxConnPerRoute > 0) {
poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
}
connManagerCopy = poolingmgr;
}
ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
if (reuseStrategyCopy == null) {
if (systemProperties) {
final String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
} else {
reuseStrategyCopy = NoConnectionReuseStrategy.INSTANCE;
}
} else {
reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
}
}
ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
if (keepAliveStrategyCopy == null) {
keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
}
AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
if (targetAuthStrategyCopy == null) {
targetAuthStrategyCopy = TargetAuthenticationStrategy.INSTANCE;
}
AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
if (proxyAuthStrategyCopy == null) {
proxyAuthStrategyCopy = ProxyAuthenticationStrategy.INSTANCE;
}
UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
if (userTokenHandlerCopy == null) {
if (!connectionStateDisabled) {
userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
} else {
userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
}
}
String userAgentCopy = this.userAgent;
if (userAgentCopy == null) {
if (systemProperties) {
userAgentCopy = System.getProperty("http.agent");
}
if (userAgentCopy == null) {
userAgentCopy = VersionInfo.getUserAgent("Apache-HttpClient",
"org.apache.http.client", getClass());
}
}
ClientExecChain execChain = createMainExec(
requestExecCopy,
connManagerCopy,
reuseStrategyCopy,
keepAliveStrategyCopy,
new ImmutableHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
targetAuthStrategyCopy,
proxyAuthStrategyCopy,
userTokenHandlerCopy);
execChain = decorateMainExec(execChain);
HttpProcessor httpprocessorCopy = this.httpprocessor;
if (httpprocessorCopy == null) {
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
if (requestFirst != null) {
for (final HttpRequestInterceptor i: requestFirst) {
b.addFirst(i);
}
}
if (responseFirst != null) {
for (final HttpResponseInterceptor i: responseFirst) {
b.addFirst(i);
}
}
b.addAll(
new RequestDefaultHeaders(defaultHeaders),
new RequestContent(),
new RequestTargetHost(),
new RequestClientConnControl(),
new RequestUserAgent(userAgentCopy),
new RequestExpectContinue());
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
if (!contentCompressionDisabled) {
if (contentDec