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{
Class>c=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;
}
}
|
|