本篇文章針對向ServiceManager注冊服務(wù) 和 獲取服務(wù)兩個流程來做總結(jié)。在這兩個過程中,ServiceManager都扮演的是服務(wù)端,與客戶端之間的通信也是通過Binder IPC。
德興網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,德興網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為德興近1000家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務(wù)好的德興做網(wǎng)站的公司定做!
在此之前先了解下Binder的進(jìn)程與線程的關(guān)系:
用戶空間 :ProcessState描述一個進(jìn)程,IPCThreadState對應(yīng)一個進(jìn)程中的一個線程。
內(nèi)核空間 :binder_proc描述一個進(jìn)程,統(tǒng)一由binder_procs全局鏈表保存,binder_thread對應(yīng)進(jìn)程的一個線程。
ProcessState與binder_proc是一一對應(yīng)的。
Binder線程池 :每個Server進(jìn)程在啟動時會創(chuàng)建一個binder線程池,并向其中注冊一個Binder線程;之后Server進(jìn)程也可以向binder線程池注冊新的線程,或者Binder驅(qū)動在探測到?jīng)]有空閑binder線程時會主動向Server進(jìn)程注冊新的的binder線程。對于一個Server進(jìn)程有一個最大Binder線程數(shù)限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。對于所有Client端進(jìn)程的binder請求都是交由Server端進(jìn)程的binder線程來處理的。我的理解是:binder線程是進(jìn)程進(jìn)行binder ipc時的一條數(shù)據(jù)處理路徑。
MediaPlayerService向ServiceManager注冊過程如下:
相關(guān)類:
整個過程總結(jié)如下:
1 獲取BpServiceManager 與 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同時會創(chuàng)建ProcessState對象和BpBinder對象。然后通過BpBinder執(zhí)行transact,把真正工作交給IPCThreadState來處理。
2 BpBinder transact
Binder代理類調(diào)用transact()方法,真正工作還是交給IPCThreadState來進(jìn)行transact工作。
3 通過IPCThreadState 包裝并轉(zhuǎn)換數(shù)據(jù)并進(jìn)行transact事務(wù)處理
每個線程都有一個IPCThreadState,每個IPCThreadState中都有一對Parcel變量:mIn、mOut。相當(dāng)于兩根數(shù)據(jù)管道:
最后執(zhí)行talkWithDriver。
writeTransactionData:將BC Protocol + binder_transaction_data結(jié)構(gòu)體 寫入mOut, 然后執(zhí)行waitForResponse:
由talkWithDriver將數(shù)據(jù)進(jìn)一步封裝到binder_write_read結(jié)構(gòu)體,通過ioctl(BINDER_WRITE_READ)與驅(qū)動通信。同時等待驅(qū)動返回的接收BR命令,從mIn取出返回的數(shù)據(jù)。
mIn包裝的數(shù)據(jù)結(jié)構(gòu)(注冊服務(wù)handle = 0 ,code 為ADD_SERVICE_TRANSACTION):
4 Binder Driver
把binder_write_read結(jié)構(gòu)體write_buffer里數(shù)據(jù)取出來,分別得到BC命令和封裝好數(shù)據(jù)的事務(wù)binder_transaction_data, 然后根據(jù)handler,在當(dāng)前binder_proc中,找到相應(yīng)的binder_ref,由binder_ref再找到目標(biāo)binder_node實體,由目標(biāo)binder_node再找到目標(biāo)進(jìn)程binder_proc。然后就是插入數(shù)據(jù):當(dāng)binder驅(qū)動可以找到合適的線程,就會把binder_transaction節(jié)點插入到servciemanager的線程的todo隊列中,如果找不到合適的線程,就把節(jié)點之間插入servciemanager的binder_proc的todo隊列。
5 ServiceManager
經(jīng)過Binder Driver的處理,數(shù)據(jù)已經(jīng)到了ServiceManager進(jìn)程,在BR_TRANSACTION的引導(dǎo)下,在binder_loop()中執(zhí)行binder_parser()取出數(shù)據(jù),執(zhí)行do_add_service()操作,最終向 svcinfo 列表中添加已經(jīng)注冊的服務(wù)(沒有數(shù)據(jù)的返回)。最后發(fā)送 BR_REPLY 命令喚醒等待的線程,通知注冊成功。結(jié)束MediaPlayerService進(jìn)程 waitForResponse()的狀態(tài),整個注冊過程結(jié)束。
獲取服務(wù)的過程與注冊類似,首先 ServiceManager 向 Binder 驅(qū)動發(fā)送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令,同時獲取服務(wù)的線程進(jìn)入等待狀態(tài) waitForResponse()。Binder 驅(qū)動收到請求命令向 ServiceManager 的發(fā)送 BC_TRANSACTION 查詢已注冊的服務(wù),會區(qū)分請求服務(wù)所屬進(jìn)程情況。
查詢到直接響應(yīng) BR_REPLY 喚醒等待的線程。若查詢不到將與 binder_procs 鏈表中的服務(wù)進(jìn)行一次通訊再響應(yīng)。
以startService為例來簡單總結(jié)下執(zhí)行流程:
3.1 從方法執(zhí)行流程來看:
Client :
1 AMP.startService 標(biāo)記方法以及通過Parcel包裝數(shù)據(jù);
2 BinderProxy.transact 實際調(diào)用native的 android_os_BinderProxy_transact 傳遞數(shù)據(jù);
3 獲取BpServiceManager 與 BpBinder 同時會創(chuàng)建ProcessState。然后通過BpBinder執(zhí)行transact,把真正工作交給IPCThreadState來處理;
4 IPC.transact 主要執(zhí)行writeTransactionData,將上層傳來的數(shù)據(jù)重新包裝成binder_transaction_data,并將BC Protocol + binder_transaction_data結(jié)構(gòu)體 寫入mOut;
5 IPC waitForResponse talkWithDriver + 等待返回數(shù)據(jù);
6 talkWithDriver 將數(shù)據(jù)進(jìn)一步封裝成binder_write_read,通過ioctl(BINDER_WRITE_READ)與驅(qū)動通信;
Kernel :
7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;
8 binder_ioctl_write_read 把用戶空間數(shù)據(jù)ubuf拷貝到內(nèi)核空間bwr;
9 binder_thread_write 當(dāng)bwr寫緩存有數(shù)據(jù),則執(zhí)行binder_thread_write;當(dāng)寫失敗則將bwr數(shù)據(jù)寫回用戶空間并退出;
10 binder_transaction 找到目標(biāo)進(jìn)程binder_proc并插入數(shù)據(jù)到目標(biāo)進(jìn)程的線程todo隊列,最終執(zhí)行到它
時,將發(fā)起端數(shù)據(jù)拷貝到接收端進(jìn)程的buffer結(jié)構(gòu)體;
11 binder_thread_read 根據(jù)binder_transaction結(jié)構(gòu)體和binder_buffer結(jié)構(gòu)體數(shù)據(jù)生成新的binder_transaction_data結(jié)構(gòu)體,寫入bwr的read_buffer,當(dāng)bwr讀緩存有數(shù)據(jù),則執(zhí)行binder_thread_read;當(dāng)讀失敗則再將bwr數(shù)據(jù)寫回用戶空間并退出;最后,把內(nèi)核數(shù)據(jù)bwr拷貝到用戶空間ubuf。
12 binder_thread_write + binder_ioctl BR命令和數(shù)據(jù)傳遞
Server:
13 IPC.executeCommand 解析kernel傳過來的binder_transaction_data數(shù)據(jù),找到目標(biāo)BBinder并調(diào)用其transact()方法;
14 IPC.joinThreadPool 采用循環(huán)不斷地執(zhí)行g(shù)etAndExecuteCommand()方法, 處理事務(wù)。當(dāng)bwr的讀寫buffer都沒有數(shù)據(jù)時,則阻塞在binder_thread_read的wait_event過程. 另外,正常情況下binder線程一旦創(chuàng)建則不會退出.
15 BBinder.transact 到Binder.exeTransact 調(diào)用 AMN.onTransact
16 AMN.onTransact 把數(shù)據(jù)傳遞到AMS.starService去執(zhí)行
17 AMS.starService Server處理了Client的請求了
然后原路replay回去,talkWithDriver 到Kernel ,然后找到Client進(jìn)程,把數(shù)據(jù)拷貝到read_buffer里,最終喚醒IPC,把反饋傳遞回AMP.startService。完成啟動服務(wù)。
3.2 從通信協(xié)議流程來看:
非oneWay:
oneway:
oneway與非oneway區(qū)別: 都是需要等待Binder Driver的回應(yīng)消息BR_TRANSACTION_COMPLETE. 主要區(qū)別在于oneway的通信收到BR_TRANSACTION_COMPLETE則返回,而不會再等待BR_REPLY消息的到來. 另外,oneway的binder IPC則接收端無法獲取對方的pid.
3.3 從數(shù)據(jù)流來看
從用戶空間開始:
進(jìn)入驅(qū)動后:
回到用戶空間:
參考:
由身份或持有的令牌確認(rèn)享有的權(quán)限,登錄過程實質(zhì)上的目的也是為了確認(rèn)權(quán)限。
Cookie是客戶端給服務(wù)器用的,setCookie是服務(wù)器給客戶端用的。cookie由服務(wù)器處理,客戶端負(fù)責(zé)存儲
客戶端發(fā)送cookie:賬戶和密碼
服務(wù)端收到后確認(rèn)登錄setCookie:sessionID=1,記下sessionID
客戶端收到sessionID后記錄,以后請求服務(wù)端帶上對比記錄下sessionID,說明已經(jīng)登錄
會話管理:登錄狀態(tài),購物車
個性化:用戶偏好,主題
Tracking:分析用戶行為
XXS:跨腳本攻擊,及使用JavaScript拿到瀏覽器的cookie之后,發(fā)送到自己的網(wǎng)站,以這種方式來盜用用戶Cookie。Server在發(fā)送Cookie時,敏感的Cookie加上HttpOnly,這樣Cookie只能用于http請求,不能被JavaScript調(diào)用
XSRF:跨站請求偽造。Referer 從哪個網(wǎng)站跳轉(zhuǎn)過來
兩種方式:Basic和Bearer
首先第三方網(wǎng)站向授權(quán)網(wǎng)站申請第三方授權(quán)合作,拿到授權(quán)方頒發(fā)的client_id和client_secret(一般都是appid+appkey的方式)。
在這就過程中申請的client_secret是服務(wù)器持有的,安全起見不能給客戶端,用服務(wù)端去和授權(quán)方獲取用戶信息,再傳給客戶端,包括④,⑤的請求過程也是需要加密的。這才是標(biāo)準(zhǔn)的授權(quán)過程。
有了access_token之后,就可以向授權(quán)方發(fā)送請求來獲取用戶信息
步驟分析就是上面的內(nèi)容,這里把第4,6,8請求的參數(shù)分析一下
第④步參數(shù):
response_type:指授權(quán)類型,必選,這里填固定值‘code’
client_id:指客戶端id,必選,這里填在平臺報備時獲取的appid
redirect_uri:指重定向URI,可選
scope:指申請的權(quán)限范圍,可選
state:指客戶端當(dāng)前狀態(tài),可選,若填了,則認(rèn)證服務(wù)器會原樣返回該值
第⑥步參數(shù):
grant_type:指使用哪種授權(quán)模式,必選,這里填固定值‘a(chǎn)uthorization_code’
code:指從第⑤步獲取的code,必選
redirect_uri:指重定向URI,必選,這個值需要和第④步中的redirect_uri保持一致
client_id:指客戶端id,必選,這里填在平臺報備時獲取的appid
client_secret:指客戶端密鑰,必選,這里填在平臺報備時獲取的appkey
第⑧步參數(shù):
access_token:指訪問令牌,必選,這里填第⑦步獲取的access_token
token_type:指令牌類型,必選,大小寫不敏感,bearer類型 / mac類型
expires_in:指過期時間,單位秒,當(dāng)其他地方已設(shè)置過期時間,此處可省略該參數(shù)
refresh_token:指更新令牌,可選,用即將過期token換取新token
scope:指權(quán)限范圍,可選,第④步中若已申請過某權(quán)限,此處可省略該參數(shù)
我們在上面的第八步中會有refresh_token的參數(shù),這個在實際操作中也比較常見
有時候我們在自己的項目中,將登陸和授權(quán)設(shè)計成類似OAuth2的過程,不過去掉Authorization code。登陸成功返回access_token,然后客戶端再請求時,帶上access_token。
我們常常會說到TCP/IP,那到底是什么呢。這就需要講到網(wǎng)絡(luò)分層模型。TCP在傳輸層,IP在網(wǎng)絡(luò)層。那為什么需要分層?因為網(wǎng)絡(luò)不穩(wěn)定,導(dǎo)致需要重傳的問題。為了提高傳輸效率我們就需要分塊,在傳輸層中就會進(jìn)行分塊。TCP還有兩個重要的內(nèi)容就是三次握手,四次分手。
HTTPS 協(xié)議是由 HTTP 加上TLS/SSL協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,主要通過數(shù)字證書、加密算法、非對稱密鑰等技術(shù)完成互聯(lián)網(wǎng)數(shù)據(jù)傳輸加密,實現(xiàn)互聯(lián)網(wǎng)傳輸安全保護(hù)
1.客戶端通過發(fā)送Client Hello報文開始SSL通信。報文中包含客戶端支持的SSL的指定版本、加密組件列表(所使用的加密算法及密鑰長度),客戶端隨機(jī)數(shù),hash算法。
2.服務(wù)器可進(jìn)行SSL通信時,會以Server Hello報文作為應(yīng)答。和客戶端一樣,在報文中包含SSL版本以及加密組件,服務(wù)端隨機(jī)數(shù)。服務(wù)器的加密組件內(nèi)容是從接收到客戶端加密組件內(nèi)篩選出來的。
3.之后服務(wù)器發(fā)送Certificate報文。報文中包含公開密鑰證書。一般實際有三層證書嵌套,其實像下面圖二直接用根證書機(jī)構(gòu)簽名也是可以的,但是一般根證書機(jī)構(gòu)比較忙,需要類似中介的證書機(jī)構(gòu)來幫助。
4.最后服務(wù)器發(fā)送Server Hello Done報文通知客戶端,最初階段的SSL握手協(xié)商部分結(jié)束。
5.SSL第一次握手結(jié)束后,客戶端以Client Key Exchange報文作為回應(yīng)。報文中包含通信加密中使用的一種被稱為Pre-master secret的隨機(jī)密碼串。該報文已用步驟3中的公開密鑰進(jìn)行加密。
6.接著客戶端繼續(xù)發(fā)送Change Cipher Spec報文。該報文會提示服務(wù)器,在此報文之后的通信會采用Pre-master secret密鑰加密。
7.客戶端發(fā)送Finished報文。該報文包含連接至今全部報文的整體校驗值。這次握手協(xié)商是否能夠成功,要以服務(wù)器是否能夠正確解密報文作為判定標(biāo)準(zhǔn)。
8.服務(wù)器同樣發(fā)送Change Cipher Spec報文。
9.服務(wù)器同樣發(fā)送Finished報文。
10.服務(wù)器和客戶端的Finished報文交換完畢之后,SSL連接就算建立完成。當(dāng)然,通信會受到SSL的保護(hù)。從此處開始進(jìn)行應(yīng)用層協(xié)議的通信,即發(fā)送HTTP響應(yīng)。
11.應(yīng)用層協(xié)議通信,即發(fā)送HTTP響應(yīng)。
12.最后由客戶端斷開連接。斷開連接時,發(fā)送close_notify報文。這步之后再發(fā)送TCP FIN報文來關(guān)閉與TCP的通信。
利用客戶端隨機(jī)數(shù),服務(wù)端隨機(jī)數(shù),per-master secret隨機(jī)數(shù)生成master secret,再生成客戶端加密密鑰,服務(wù)端加密密鑰,客戶端MAC secert,服務(wù)端MAC secert。MAC secert用于做報文摘要,這樣能夠查知報文是否遭到篡改,從而保護(hù)報文的完整性。
Android網(wǎng)絡(luò)請求知識(一)HTTP基礎(chǔ)概念
Android網(wǎng)絡(luò)請求知識(二)對稱和非對稱加密、數(shù)字簽名,Hash,Base64編碼
Android網(wǎng)絡(luò)請求知識(三)授權(quán),TCP/IP,HTTPS建立過程
android socket通信協(xié)議的封裝和解析,其實是和java一樣的,都是通過http中的相關(guān)知識來封裝和解析,主要是通過多次握手,如下代碼:
import?java.io.BufferedReader;
import?java.io.BufferedWriter;
import?java.io.IOException;
import?java.io.InputStreamReader;
import?java.io.OutputStreamWriter;
import?java.io.PrintWriter;
import?java.net.ServerSocket;
import?java.net.Socket;
import?java.util.ArrayList;
import?java.util.List;
import?java.util.concurrent.ExecutorService;
import?java.util.concurrent.Executors;
public?class?Main?{
private?static?final?int?PORT?=?9999;
private?ListSocket?mList?=?new?ArrayListSocket();
private?ServerSocket?server?=?null;
private?ExecutorService?mExecutorService?=?null;?//thread?pool
public?static?void?main(String[]?args)?{
new?Main();
}
public?Main()?{
try?{
server?=?new?ServerSocket(PORT);
mExecutorService?=?Executors.newCachedThreadPool();??//create?a?thread?pool
System.out.println("服務(wù)器已啟動...");
Socket?client?=?null;
while(true)?{
client?=?server.accept();
//把客戶端放入客戶端集合中
mList.add(client);
mExecutorService.execute(new?Service(client));?//start?a?new?thread?to?handle?the?connection
}
}catch?(Exception?e)?{
e.printStackTrace();
}
}
class?Service?implements?Runnable?{
private?Socket?socket;
private?BufferedReader?in?=?null;
private?String?msg?=?"";
public?Service(Socket?socket)?{
this.socket?=?socket;
try?{
in?=?new?BufferedReader(new?InputStreamReader(socket.getInputStream()));
//客戶端只要一連到服務(wù)器,便向客戶端發(fā)送下面的信息。
msg?=?"服務(wù)器地址:"?+this.socket.getInetAddress()?+?"come?toal:"
+mList.size()+"(服務(wù)器發(fā)送)";
this.sendmsg();
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
@Override
public?void?run()?{
try?{
while(true)?{
if((msg?=?in.readLine())!=?null)?{
//當(dāng)客戶端發(fā)送的信息為:exit時,關(guān)閉連接
if(msg.equals("exit"))?{
System.out.println("ssssssss");
mList.remove(socket);
in.close();
msg?=?"user:"?+?socket.getInetAddress()
+?"exit?total:"?+?mList.size();
socket.close();
this.sendmsg();
break;
//接收客戶端發(fā)過來的信息msg,然后發(fā)送給客戶端。
}?else?{
msg?=?socket.getInetAddress()?+?":"?+?msg+"(服務(wù)器發(fā)送)";
this.sendmsg();
}
}
}
}?catch?(Exception?e)?{
e.printStackTrace();
}
}
/**
*?循環(huán)遍歷客戶端集合,給每個客戶端都發(fā)送信息。
*/
public?void?sendmsg()?{
System.out.println(msg);
int?num?=mList.size();
for?(int?index?=?0;?index??num;?index?++)?{
Socket?mSocket?=?mList.get(index);
PrintWriter?pout?=?null;
try?{
pout?=?new?PrintWriter(new?BufferedWriter(
new?OutputStreamWriter(mSocket.getOutputStream())),true);
pout.println(msg);
}catch?(IOException?e)?{
e.printStackTrace();
}
}
}
}????
}