網(wǎng)絡(luò)編程
網(wǎng)絡(luò)分層
網(wǎng)絡(luò)分層有不同的模型箱季,有的分為7層,有的分為5層。
- 物理層 該層負(fù)責(zé)比特流在節(jié)點(diǎn)間的傳輸畸写,即負(fù)責(zé)物理傳輸。通俗來講就是把計(jì)算機(jī)連接起來的物理手段
- 數(shù)據(jù)鏈路層 該層控制網(wǎng)絡(luò)層和物理層之間的通信
- 網(wǎng)絡(luò)層 該層決定如何將數(shù)據(jù)從發(fā)送方路由到接收方
- 傳輸層 該層為兩臺(tái)主機(jī)上的應(yīng)用程序提供端到端的通信氓扛。傳輸層有兩個(gè)傳輸協(xié)議:TCP/UDP
- 應(yīng)用層 應(yīng)用層收到傳輸層數(shù)據(jù)后枯芬,對數(shù)據(jù)進(jìn)行解讀。解讀必須事先規(guī)定好格式采郎。應(yīng)用層是規(guī)定應(yīng)用程序的數(shù)據(jù)格式的千所,主要協(xié)議有HTTP、FTP蒜埋、Telnet淫痰、SMTP、POP3等
TCP的三次握手與四次揮手
TCP三次握手與四次揮手的過程
連接復(fù)用 keepalive connections
HTTP協(xié)議原理
- HTTP URL的格式
http://host[":"post][abs_path]
HTTP請求報(bào)文
HTTP響應(yīng)報(bào)文
源碼解析OkHttp
OkHttp的請求網(wǎng)絡(luò)流程
- 當(dāng)我們要請求網(wǎng)絡(luò)的時(shí)候需要用OkHttpClient.newCall(request)進(jìn)行execute或者enqueue操作整份。當(dāng)調(diào)用newCall方法時(shí)待错,我們從源碼中可以看出調(diào)用了如下代碼
@Override
public Call newCall(Request request) {
return new RealCall(this, request)籽孙;
}
可以看到實(shí)際返回的是一個(gè)RealCall類。我們調(diào)用enqueue異步請求網(wǎng)絡(luò)實(shí)際上是調(diào)用RealCall的enqueue方法
void enqueue(Callback responseCallback, boolean forWebSocket) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed")火俄;
executed = true蚯撩;
}
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
}
從這可以看出最終的請求是dispatcher來完成的烛占,我們來看看dispatcher
- Dispatcher 任務(wù)調(diào)度
Dispatcher主要用于控制并發(fā)的請求胎挎,它主要維護(hù)了以下變量
/** 最大并發(fā)請求數(shù)*/
private int maxRequests = 64;
/** 每個(gè)主機(jī)的最大請求數(shù)*/
private int maxRequestsPerHost = 5忆家;
/** 消費(fèi)者線程池 */
private ExecutorService executorService犹菇;
/** 將要運(yùn)行的異步請求隊(duì)列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/**正在運(yùn)行的異步請求隊(duì)列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>()芽卿;
/** 正在運(yùn)行的同步請求隊(duì)列 */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>()揭芍;
來看Dispatcher的構(gòu)造方法
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), Util.threadFactory
("OkHttp Dispatcher", false))卸例;
}
return executorService称杨;
}
從dispathcer的構(gòu)造方法中可以看到,dispathcer中持有一個(gè)線程池筷转,這個(gè)線程池可以使用自己設(shè)定的線程池姑原。如果沒有設(shè)定線程池,則會(huì)在請求網(wǎng)絡(luò)前自己創(chuàng)建默認(rèn)線程池呜舒。這個(gè)線程池比較適合執(zhí)行大量的耗時(shí)比較少的任務(wù)锭汛。來看它的enqueue方法
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call)
< maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call)袭蝗;
} else {
readyAsyncCalls.add(call)唤殴;
}
}
當(dāng)正在運(yùn)行的異步請求隊(duì)列中的數(shù)量小于64并且正在運(yùn)行的請求主機(jī)數(shù)小于5時(shí),把請求加載到runningAsyncCalls中并在線程池中執(zhí)行到腥,否則就加入到readyAsyncCalls中進(jìn)行緩存等待朵逝。
來看AsyncCall的execute方法
@Override
protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain(forWebSocket)乡范;//1
if (canceled) {
signalledCallback = true配名;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true篓足;
responseCallback.onResponse(RealCall.this, response)段誊;
}
} catch (IOException e) {
if (signalledCallback) {
logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e)栈拖;
}
} finally {
client.dispatcher().finished(this)连舍;
}
}
我們可以看到 getResponseWithInterceptorChain方法返回了Response,這說明正在請求網(wǎng)絡(luò)
- Interceptor攔截器
接下來我們再看看getResponseWithInterceptorChain方法
private Response getResponseWithInterceptorChain(boolean forWebSocket)
throws IOException {
Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest,
forWebSocket);
return chain.proceed(originalRequest)索赏;
}
在這個(gè)方法中創(chuàng)建了ApplicationInterceptorChain盼玄。這是一個(gè)攔截器鏈,這個(gè)類也是RealCall的內(nèi)部類潜腻,接下來執(zhí)行了它的proceed方法
public Response proceed(Request request) throws IOException {
if (index < client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1,
request, forWebSocket)埃儿;
//從攔截器列表中取出攔截器
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain)融涣;//1
if (interceptedResponse == null) {
throw new NullPointerException("application interceptor " + interceptor
+ " returned null")童番;
}
return interceptedResponse;
}
return getResponse(request, forWebSocket)威鹿;
}
可以看到proceed方法每次從攔截器列表中取出攔截器剃斧。當(dāng)存在多個(gè)攔截器時(shí)都會(huì)//1處阻塞,并等待下一個(gè)攔截器的調(diào)用返回忽你。
攔截器時(shí)一種能夠監(jiān)控幼东、重寫、重試調(diào)用的機(jī)制科雳。攔截器用來添加根蟹、移除、轉(zhuǎn)換請求和響應(yīng)的頭部信息糟秘。我們可以看到return getResponse(request,forWebSocket)如果沒有更多攔截器的話简逮,就會(huì)執(zhí)行網(wǎng)絡(luò)請求。
我們看下getResponse方法
Response getResponse(Request request, boolean forWebSocket) throws
IOException {
...
engine = new HttpEngine(client, request, false, false, forWebSocket,
null, null, null)蚌堵;
int followUpCount = 0买决;
while (true) {
if (canceled) {
engine.releaseStreamAllocation()沛婴;
throw new IOException("Canceled")吼畏;
}
boolean releaseConnection = true;
try {
engine.sendRequest()嘁灯;
engine.readResponse()泻蚊;
releaseConnection = false;
} catch (RequestException e) {
throw e.getCause()丑婿;
} catch (RouteException e) {
...
}
}
在獲取網(wǎng)絡(luò)響應(yīng)的方法中 我們可以看到創(chuàng)建了HttpEngine類性雄,并調(diào)用了HttpEngine的sendRequest方法和readResponse方法
- 緩存策略
我們先來看HttpEngine的sendRequest方法
public void sendRequest() throws RequestException, RouteException, IOException {
if (cacheStrategy != null) return; // Already sent.
if (httpStream != null) throw new IllegalStateException()羹奉;
Request request = networkRequest(userRequest)秒旋;
//獲取 client 中的 Cache, 同時(shí) Cache 在初始化時(shí)會(huì)讀取緩存目錄中曾經(jīng)請求過的所有信息
InternalCache responseCache = Internal.instance.internalCache(client)诀拭;
Response cacheCandidate = responseCache != null
? responseCache.get(request): null迁筛;//1
long now = System.currentTimeMillis();
cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).
get()耕挨;
//網(wǎng)絡(luò)請求
networkRequest = cacheStrategy.networkRequest细卧;
//緩存的響應(yīng)
cacheResponse = cacheStrategy.cacheResponse尉桩;
if (responseCache != null) {
//記錄當(dāng)前請求是網(wǎng)絡(luò)發(fā)起還是緩存發(fā)起
responseCache.trackResponse(cacheStrategy);
}
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body())贪庙;
}
//不進(jìn)行網(wǎng)絡(luò)請求并且緩存不存在或者過期蜘犁, 則返回 504 錯(cuò)誤
if (networkRequest == null && cacheResponse == null) {
userResponse = new Response.Builder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_BODY)
.build();
return止邮;
}
// 不進(jìn)行網(wǎng)絡(luò)請求而且緩存可以使用这橙, 則直接返回緩存
if (networkRequest == null) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.build();
userResponse = unzip(userResponse)导披;
return析恋;
}
//需要訪問網(wǎng)絡(luò)時(shí)
boolean success = false;
try {
httpStream = connect()盛卡;
httpStream.setHttpEngine(this)助隧;
...
}
}
這里可以看出緩存是基于Map的 key就是請求中url的md5,value是在文件中查詢到的緩存滑沧,頁面置換基于LRU算法并村。
接著來看HttpEngine的readResponse方法
public void readResponse() throws IOException {
...
else{
//讀取網(wǎng)絡(luò)響應(yīng)
networkResponse = readNetworkResponse();
}
receiveHeaders(networkResponse.headers())滓技;
if (cacheResponse != null) {
//檢查緩存是否可用哩牍。 如果可用, 就用當(dāng)前緩存的 Response令漂,關(guān)閉網(wǎng)絡(luò)連接膝昆,釋放連接
if (validate(cacheResponse, networkResponse)) {//1
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.headers(combine(cacheResponse.headers(), networkResponse.
headers()))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
networkResponse.body().close()叠必;
releaseStreamAllocation()荚孵;
InternalCache responseCache = Internal.instance.internalCache(client);
responseCache.trackConditionalCacheHit()纬朝;
responseCache.update(cacheResponse, stripBody(userResponse))收叶;
userResponse = unzip(userResponse);
return共苛;
} else {
closeQuietly(cacheResponse.body())判没;
}
}
userResponse = networkResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build();
if (hasBody(userResponse)) {
maybeCache()隅茎;
userResponse = unzip(cacheWritingResponse(storeRequest, userResponse))澄峰;
}
}
這個(gè)方法主要用來解析HTTP響應(yīng)報(bào)頭,如果有緩存可用辟犀,則用緩存的數(shù)據(jù)并更新緩存俏竞,否則就用網(wǎng)絡(luò)請求返回的數(shù)據(jù)
解析Retrofit
Retrofit是Square公司開發(fā)的一款針對Android網(wǎng)絡(luò)請求的框架。Retrofit的底層是基于OkHttp實(shí)現(xiàn)的,它更多使用運(yùn)行時(shí)注解的方式提供功能
我們在使用Retrofit請求網(wǎng)絡(luò)時(shí)胞此,首先需要寫請求接口
public interface IpService {
@GET("getIpInfo.php?ip=59.108.54.37")
Call<IpModel> getIpMsg()臣咖;
接著我們創(chuàng)建Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
可以看出來Retrofit是通過建造者模式構(gòu)建出來的漱牵,我們看一下Builder方法做了什么
public Builder() {
this(Platform.get())夺蛇;
}
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM酣胀;
}
private static Platform findPlatform() {
try {
Class.forName("android.os.Build")刁赦;
if (Build.VERSION.SDK_INT != 0) {
return new Android();
}
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("java.util.Optional")闻镶;
return new Java8()甚脉;
} catch (ClassNotFoundException ignored) {
}
try {
Class.forName("org.robovm.apple.foundation.NSObject");
return new IOS()铆农;
} catch (ClassNotFoundException ignored) {
}
return new Platform()牺氨;
}
可以看到Platform的get方法最終調(diào)用的是findPlatform方法,會(huì)根據(jù)不同的運(yùn)行平臺(tái)來提供不同的線程池墩剖,接下來看build方法
public Retrofit build() {
if (baseUrl == null) {//1
throw new IllegalStateException("Base URL required.")猴凹;
}
okhttp3.Call.Factory callFactory = this.callFactory;//2
if (callFactory == null) {
callFactory = new OkHttpClient()岭皂;//3
}
Executor callbackExecutor = this.callbackExecutor郊霎;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();//4
}
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.
adapterFactories)爷绘;//5
adapterFactories.add(platform.defaultCallAdapterFactory
(callbackExecutor))书劝;
List<Converter.Factory> converterFactories = new ArrayList<>(this.
converterFactories);//6
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly)土至;
}
這里1處可看出baseUrl是必須指定的
Call的創(chuàng)建過程
我們創(chuàng)建Retrofit實(shí)例并調(diào)用如下代碼來生成接口的動(dòng)態(tài)代理對象
IpService ipService = retrofit.create(IpService.class)购对;
我們看下Retrofit的create方法
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service)毙籽;
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]
{ service },
new InvocationHandler() {
private final Platform platform = Platform.get()洞斯;
@Override
public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args)坑赡;
}
ServiceMethod serviceMethod = loadServiceMethod(method);//1
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args)么抗;
return serviceMethod.callAdapter.adapt(okHttpCall)毅否;
}
});
}
我們可以看到create方法返回了一個(gè)動(dòng)態(tài)代理對象蝇刀。當(dāng)我們調(diào)用IpService的getIpMsg方法時(shí)螟加,最終會(huì)調(diào)用InvocationHandler的invoke方法