配色: 字号:
Android网络之HttpUrlConnection和Socket关系解析
2016-12-17 | 阅:  转:  |  分享 
  
Android网络之HttpUrlConnection和Socket关系解析



多年以前Android的网络请求只有Apache开源的HttpClient和JDK的HttpUrlConnection,近几年随着OkHttp的流行Android在高版本的SDK中加入了OkHttp。但在Android官方文档中推荐使用HttpUrlConnection并且其会一直被维护,所以在学习Android网络相关的知识时我们队HttpUrlConnection要有足够的了解。。。。



前几天因为时间的关系只画了图HttpUrlConnection和Socket的关系图,本来说好的第二天续写,结果一直拖到了周末晚上。幸好时间还来的及,趁这短时间影响深刻,将自己解析代码过程记录下来。(PS:解析的过程有什么地方不明白的可以看看HttpUrlConnection和Socket的关系图图中讲出的过程和这次代码分析的过程是一样的,只不过代码讲述更加详细。所有源码都是来自Android4.0.4。有代码就有真相~!~!)



类结构图



先给大家展示一张相关类的结构图:

HttpUrlConnection和Socket关系类图



HttpUrlConnection使用



在分析代码的时候我希望首相脑海中要有一个URL的请求过程。

这是我在网上摘的一个HttpUrlConnection请求小Demo:



publicclassEsmTest{

/

通过HttpURLConnection模拟post表单提交

@throwsException

/

@Test

publicvoidsendEms()throwsException{

Stringwen="MS2201828";

StringbtnSearch="EMS快递查询";

URLurl=newURL("http://www.kd185.com/ems.php");

HttpURLConnectionconn=(HttpURLConnection)url.openConnection();

conn.setRequestMethod("POST");//提交模式

//conn.setConnectTimeout(10000);//连接超时单位毫秒

//conn.setReadTimeout(2000);//读取超时单位毫秒

conn.setDoOutput(true);//是否输入参数

StringBufferparams=newStringBuffer();

//表单参数与get形式一样

params.append("wen").append("=").append(wen).append("&")

.append("btnSearch").append("=").append(btnSearch);

byte[]bypes=params.toString().getBytes();

conn.getOutputStream().write(bypes);//输入参数

InputStreaminStream=conn.getInputStream();

System.out.println(newString(StreamTool.readInputStream(inStream),"gbk"));



}



publicvoidsendSms()throwsException{

Stringmessage="货已发到";

message=URLEncoder.encode(message,"UTF-8");

System.out.println(message);

Stringpath="http://localhost:8083/DS_Trade/mobile/sim!add.do?message="+message;

URLurl=newURL(path);

HttpURLConnectionconn=(HttpURLConnection)url.openConnection();

conn.setConnectTimeout(51000);

conn.setRequestMethod("GET");

InputStreaminStream=conn.getInputStream();

byte[]data=StreamTool.readInputStream(inStream);

Stringresult=newString(data,"UTF-8");

System.out.println(result);

}



URL产生请求



/URL.java/

/

创建一个新的URL实例

/

publicURL(Stringspec)throwsMalformedURLException{

this((URL)null,spec,null);

}

publicURL(URLcontext,Stringspec,URLStreamHandlerhandler)throwsMalformedURLException{

if(spec==null){

thrownewMalformedURLException();

}

if(handler!=null){

streamHandler=handler;

}

spec=spec.trim();

//获取url的协议类型,http,https

protocol=UrlUtils.getSchemePrefix(spec);

//请求开始部分的位置

intschemeSpecificPartStart=protocol!=null?(protocol.length()+1):0;

if(protocol!=null&&context!=null&&!protocol.equals(context.protocol)){

context=null;

}

if(context!=null){

set(context.protocol,context.getHost(),context.getPort(),context.getAuthority(),

context.getUserInfo(),context.getPath(),context.getQuery(),

context.getRef());

if(streamHandler==null){

streamHandler=context.streamHandler;

}

}elseif(protocol==null){

thrownewMalformedURLException("Protocolnotfound:"+spec);

}

//这里为重点,获取StreamHandler

if(streamHandler==null){

setupStreamHandler();

if(streamHandler==null){

thrownewMalformedURLException("Unknownprotocol:"+protocol);

}

}

try{

//对url的处理

streamHandler.parseURL(this,spec,schemeSpecificPartStart,spec.length());

}catch(Exceptione){

thrownewMalformedURLException(e.toString());

}

}



voidsetupStreamHandler(){

//从缓存中获取

streamHandler=streamHandlers.get(protocol);

if(streamHandler!=null){

return;

}

//通过工厂方法创建

if(streamHandlerFactory!=null){

streamHandler=streamHandlerFactory.createURLStreamHandler(protocol);

if(streamHandler!=null){

streamHandlers.put(protocol,streamHandler);

return;

}

}

//在同名包下检测一个可用的hadnler

StringpackageList=System.getProperty("java.protocol.handler.pkgs");

ClassLoadercontextClassLoader=Thread.currentThread().getContextClassLoader();

if(packageList!=null&&contextClassLoader!=null){

for(StringpackageName:packageList.split("\\|")){

StringclassName=packageName+"."+protocol+".Handler";

try{

Classc=contextClassLoader.loadClass(className);

streamHandler=(URLStreamHandler)c.newInstance();

if(streamHandler!=null){

streamHandlers.put(protocol,streamHandler);

}

return;

}catch(IllegalAccessExceptionignored){

}catch(InstantiationExceptionignored){

}catch(ClassNotFoundExceptionignored){

}

}

}

//如果还是没有创建成功那么new一个handler

if(protocol.equals("file")){

streamHandler=newFileHandler();

}elseif(protocol.equals("ftp")){

streamHandler=newFtpHandler();

}elseif(protocol.equals("http")){

streamHandler=newHttpHandler();

}elseif(protocol.equals("https")){

streamHandler=newHttpsHandler();

}elseif(protocol.equals("jar")){

streamHandler=newJarHandler();

}

if(streamHandler!=null){

streamHandlers.put(protocol,streamHandler);

}

}



/

streamHandler实现类为HttpURLConnectionImpl

/

publicfinalclassHttpHandlerextendsURLStreamHandler{



@OverrideprotectedURLConnectionopenConnection(URLu)throwsIOException{

returnnewHttpURLConnectionImpl(u,getDefaultPort());

}



@OverrideprotectedURLConnectionopenConnection(URLurl,Proxyproxy)throwsIOException{

if(url==null||proxy==null){

thrownewIllegalArgumentException("url==null||proxy==null");

}

returnnewHttpURLConnectionImpl(url,getDefaultPort(),proxy);

}



@OverrideprotectedintgetDefaultPort(){

return80;

}



创建连接请求准备



/HttpURLConnectionImpl.javastart/

/

无论是get还是post都需要建立连接

post

/

@Override

publicfinalOutputStreamgetOutputStream()throwsIOException{

connect();

OutputStreamresult=httpEngine.getRequestBody();

if(result==null){

thrownewProtocolException("methoddoesnotsupportarequestbody:"+method);

}elseif(httpEngine.hasResponse()){

thrownewProtocolException("cannotwriterequestbodyafterresponsehasbeenread");

}

returnresult;

}

/

无论是get还是post都需要建立连接

get

/

@Override

publicfinalInputStreamgetInputStream()throwsIOException{

if(!doInput){

thrownewProtocolException("Thisprotocoldoesnotsupportinput");

}

//获取http响应

HttpEngineresponse=getResponse();

//返回400抛异常

if(getResponseCode()>=HTTP_BAD_REQUEST){

thrownewFileNotFoundException(url.toString());

}

InputStreamresult=response.getResponseBody();

if(result==null){

thrownewIOException("Noresponsebodyexists;responseCode="+getResponseCode());

}

returnresult;

}

privateHttpEnginegetResponse()throwsIOException{

//初始化http引擎

initHttpEngine();

//是否有响应头信息

if(httpEngine.hasResponse()){

returnhttpEngine;

}

try{

while(true){

//发送请求

httpEngine.sendRequest();

httpEngine.readResponse();

//为下次请求做准备

Retryretry=processResponseHeaders();

if(retry==Retry.NONE){

httpEngine.automaticallyReleaseConnectionToPool();

break;

}

//如果一个请求不能完成那么接下来为下次请求做准备

StringretryMethod=method;

OutputStreamrequestBody=httpEngine.getRequestBody();



/

AlthoughRFC261610.3.2specifiesthataHTTP_MOVED_PERM

redirectshouldkeepthesamemethod,Chrome,Firefoxandthe

RIallissueGETswhenfollowinganyredirect.

/

intresponseCode=getResponseCode();

if(responseCode==HTTP_MULT_CHOICE||responseCode==HTTP_MOVED_PERM

||responseCode==HTTP_MOVED_TEMP||responseCode==HTTP_SEE_OTHER){

retryMethod=HttpEngine.GET;

requestBody=null;

}



if(requestBody!=null&&!(requestBodyinstanceofRetryableOutputStream)){

thrownewHttpRetryException("CannotretrystreamedHTTPbody",

httpEngine.getResponseCode());

}



if(retry==Retry.DIFFERENT_CONNECTION){

httpEngine.automaticallyReleaseConnectionToPool();

}



httpEngine.release(true);



httpEngine=newHttpEngine(retryMethod,rawRequestHeaders,

httpEngine.getConnection(),(RetryableOutputStream)requestBody);

}

returnhttpEngine;

}catch(IOExceptione){

httpEngineFailure=e;

throwe;

}

}



@Override

publicfinalvoidconnect()throwsIOException{

initHttpEngine();

try{

httpEngine.sendRequest();

}catch(IOExceptione){

httpEngineFailure=e;

throwe;

}

}

/

无论是get还是post都需要初始化Http引擎

/

privatevoidinitHttpEngine()throwsIOException{

if(httpEngineFailure!=null){

throwhttpEngineFailure;

}elseif(httpEngine!=null){

return;

}

connected=true;

try{

if(doOutput){

if(method==HttpEngine.GET){

//如果要写入那么这就是一个post请求

method=HttpEngine.POST;

}elseif(method!=HttpEngine.POST&&method!=HttpEngine.PUT){

//如果你要写入,那么不是post请求也不是put请求那就抛异常吧。

thrownewProtocolException(method+"doesnotsupportwriting");

}

}

httpEngine=newHttpEngine(method,rawRequestHeaders,null,null);

}catch(IOExceptione){

httpEngineFailure=e;

throwe;

}



创建Socket连接



/HttpEngine.java/

/

Figuresoutwhattheresponsesourcewillbe,andopensasockettothat

sourceifnecessary.Preparestherequestheadersandgetsreadytostart

writingtherequestbodyifitexists.

/

publicfinalvoidsendRequest()throwsIOException{

if(responseSource!=null){

return;

}

//填充请求头和cookies

prepareRawRequestHeaders();

//初始化响应资源,计算缓存过期时间,判断是否读取缓冲中数据,或者进行网络请求

//responseSource=?

//CACHE:返回缓存信息

//CONDITIONAL_CACHE:进行网络请求如果网络请求结果无效则使用缓存

//NETWORK:返回网络请求

initResponseSource();

//请求行为记录

if(responseCacheinstanceofHttpResponseCache){

((HttpResponseCache)responseCache).trackResponse(responseSource);

}

//请求资源需要访问网络,但请求头部禁止请求。在这种情况下使用BAD_GATEWAY_RESPONSE替代

if(requestHeaders.isOnlyIfCached()&&responseSource.requiresConnection()){

if(responseSource==ResponseSource.CONDITIONAL_CACHE){

IoUtils.closeQuietly(cachedResponseBody);

}

this.responseSource=ResponseSource.CACHE;

this.cacheResponse=BAD_GATEWAY_RESPONSE;

RawHeadersrawResponseHeaders=RawHeaders.fromMultimap(cacheResponse.getHeaders());

setResponse(newResponseHeaders(uri,rawResponseHeaders),cacheResponse.getBody());

}



if(responseSource.requiresConnection()){

//socket网络连接

sendSocketRequest();

}elseif(connection!=null){

HttpConnectionPool.INSTANCE.recycle(connection);

connection=null;

}

}

privatevoidsendSocketRequest()throwsIOException{

if(connection==null){

connect();

}

if(socketOut!=null||requestOut!=null||socketIn!=null){

thrownewIllegalStateException();

}

socketOut=connection.getOutputStream();

requestOut=socketOut;

socketIn=connection.getInputStream();

if(hasRequestBody()){

initRequestBodyOut();

}

}

//打开Socket连接

protectedvoidconnect()throwsIOException{

if(connection==null){

connection=openSocketConnection();

}

}

protectedfinalHttpConnectionopenSocketConnection()throwsIOException{

HttpConnectionresult=HttpConnection.connect(

uri,policy.getProxy(),requiresTunnel(),policy.getConnectwww.baiyuewang.netTimeout());

Proxyproxy=result.getAddress().getProxy();

if(proxy!=null){

policy.setProxy(proxy);

}

result.setSoTimeout(policy.getReadTimeout());

returnresult;



/HttpConnection.java/

publicstaticHttpConnectionconnect(URIuri,Proxyproxy,booleanrequiresTunnel,

intconnectTimeout)throwsIOException{

//代理直连

if(proxy!=null){

Addressaddress=(proxy.type()==Proxy.Type.DIRECT)

?newAddress(uri)

:newAddress(uri,proxy,requiresTunnel);

returnHttpConnectionPool.INSTANCE.get(address,connectTimeout);

}

//寻找代理直连

ProxySelectorselector=ProxySelector.getDefault();

ListproxyList=selector.select(uri);

if(proxyList!=null){

for(ProxyselectedProxy:proxyList){

if(selectedProxy.type()==Proxy.Type.DIRECT){

//thesameasNO_PROXY

//TODO:iftheselectorrecommendsadirectconnection,attemptthat?

continue;

}

try{

Addressaddress=newAddress(uri,selectedProxy,requiresTunnel);

returnHttpConnectionPool.INSTANCE.get(address,connectTimeout);

}catch(IOExceptione){

//failedtoconnect,tellittotheselector

selector.connectFailed(uri,selectedProxy.address(),e);

}

}

}

//创建一个直连接

returnHttpConnectionPool.INSTANCE.get(newAddress(uri),connectTimeout);

}

privateHttpConnection(Addressconfig,intconnectTimeout)throwsIOException{

this.address=config;

SocketsocketCandidate=null;

InetAddress[]addresses=InetAddress.getAllByName(config.socketHost);

for(inti=0;i
socketCandidate=(config.proxy!=null&&config.proxy.type()!=Proxy.Type.HTTP)

?newSocket(config.proxy)

:newSocket();

try{

//DNS解析,socket连接(这块不做详细分析)

socketCandidate.connect(

newInetSocketAddress(addresses[i],config.socketPort),connectTimeout);

break;

}catch(IOExceptione){

if(i==addresses.length-1){

throwe;

}

}

}

this.socket=socketCandidate;



/HttpConnectionPool.java/

publicHttpConnectionget(HttpConnection.Addressaddress,intconnectTimeout)

throwsIOException{

//首先尝试重用现有的HTTP连接。

synchronized(connectionPool){

Listconnections=connectionPool.get(address);

if(connections!=null){

while(!connections.isEmpty()){

HttpConnectionconnection=connections.remove(connections.size()-1);

if(!connection.isStale()){//TODO:thisopdoesI/O!

//SinceSocketisrecycled,re-tagbeforeusing

finalSocketsocket=connection.getSocket();

SocketTagger.get().tag(socket);

returnconnection;

}

}

connectionPool.remove(address);

}

}

//无法找到可以复用的链接是,创建一个新的链接

returnaddress.connect(connectTimeout);

}



/HttpConnection.Address.java/

publicHttpConnectionconnect(intconnectTimeout)throwsIOException{

returnnewHttpConnection(this,connectTimeout);



输出内容获取



/HttpEngine.java/

publicfinalvoidreadResponse()throwsIOException{

//如果有响应头

if(hasResponse()){

return;

}

//readResponse之前是否sendRequest

if(responseSource==null){

thrownewIllegalStateException("readResponse()withoutsendRequest()");

}

//如果不进行网络请求直接返回

if(!responseSource.requiresConnection()){

return;

}

//刷新请求头

if(sentRequestMillis==-1){

intcontentLength=requestBodyOutinstanceofRetryableOutputStream

?((RetryableOutputStream)requestBodyOut).contentLength()

:-1;

writeRequestHeaders(contentLength);

}

//刷新请求体

if(requestBodyOut!=null){

requestBodyOut.close();

if(requestBodyOutinstanceofRetryableOutputStream){

((RetryableOutputStream)requestBodyOut).writeToSocket(requestOut);

}

}



requestOut.flush();

requestOut=socketOut;

//解析响应头

readResponseHeaders();

responseHeaders.setLocalTimestamps(sentRequestMillis,System.currentTimeMillis());

//判断响应体类型

if(responseSource==ResponseSource.CONDITIONAL_CACHE){

if(cachedResponseHeaders.validate(responseHeaders)){

if(responseCacheinstanceofHttpResponseCache){

((HttpResponseCache)responseCache).trackConditionalCacheHit();

}

//释放资源

release(true);

//返回缓存信息

setResponse(cachedResponseHeaders.combine(responseHeaders),cachedResponseBody);

return;

}else{

IoUtils.closeQuietly(cachedResponseBody);

}

}



if(hasResponseBody()){

maybeCache();//reentrant.thiscallsintousercodewhichmaycallbackintothis!

}



initContentStream(getTransferStream());

}

privateInputStreamgetTransferStream()throwsIOException{

if(!hasResponseBody()){

returnnewFixedLengthInputStream(socketIn,cacheRequest,this,0);

}



if(responseHeaders.isChunked()){

returnnewChunkedInputStream(socketIn,cacheRequest,this);

}



if(responseHeaders.getContentLength()!=-1){

returnnewFixedLengthIwww.wang027.comnputStream(socketIn,cacheRequest,this,

responseHeaders.getContentLength());

}

returnnewUnknownLengthHttpInputStream(socketIn,cacheRequest,this);

}

privatevoidinitContentStream(InputStreamtransferStream)throwsIOException{

//是否gzip压缩

if(transparentGzip&&responseHeaders.isContentEncodingGzip()){

responseHeaders.stripContentEncoding();

responseBodyIn=newGZIPInputStream(transferStream);

}else{

responseBodyIn=transferStream;

}

}

献花(0)
+1
(本文系thedust79首藏)