LocalSocket作為安卓提供的一種IPC機(jī)制,可能應(yīng)用層的同學(xué)比較陌生检诗,我實(shí)際也是在這段時(shí)間做項(xiàng)目使用到才注意到它并去了解的。不過(guò)實(shí)際上framework層里面被頻繁使用到了逢慌,例如我很久前寫的博客從源碼看安卓應(yīng)用的啟動(dòng)過(guò)程里面提到其他進(jìn)程和Zygote進(jìn)程之間的通信使用的是LocalSocket。
那么LocalSocket和Socket到底有什么不同呢涕癣?官方文檔里面其實(shí)提到了它其實(shí)是基于UNIX-domain socket的:
Non-standard class for creating an inbound UNIX-domain socket in the Linux abstract namespace.
Socket本來(lái)是用來(lái)做不同主機(jī)間的網(wǎng)絡(luò)通信的,如果有人想拿來(lái)做本機(jī)的IPC通信就會(huì)發(fā)現(xiàn)它的性能堪憂(例如實(shí)現(xiàn)binder機(jī)制做不到的傳輸大文件)距潘,因?yàn)樗枰呔W(wǎng)絡(luò)協(xié)議棧只搁、打包拆包、計(jì)算校驗(yàn)等氢惋,如果是TCP還需要走三次握手和應(yīng)答。
于是后面就發(fā)展出了UNIX-domain socket (LocalSocket)骚亿,它的api和socket的基本一致熊赖,但是本質(zhì)上只是一種IPC通信,不可和外部主機(jī)通信震鹉,但是因?yàn)镮PC通信是可靠通信,直接將數(shù)據(jù)拷貝到目標(biāo)進(jìn)程內(nèi)存即可传趾,所以沒(méi)有之前說(shuō)的那些耗時(shí)的操作。
使用方式
我們先來(lái)看看它的使用方式:
private void demoClient() throws IOException {
LocalSocket client = new LocalSocket();
client.connect(new LocalSocketAddress("me.linjw.localsocket"));
client.getOutputStream().write(123);
int read = client.getInputStream().read();
Log.d(TAG, "response from server : " + read);
client.close();
}
private void demoServer() throws IOException {
LocalServerSocket server = new LocalServerSocket("me.linjw.localsocket");
LocalSocket client = server.accept();
int read = client.getInputStream().read();
Log.d(TAG, "request from client :" + read);
client.getOutputStream().write(read + 1);
client.getOutputStream().flush();
client.close();
}
// 打印
// request from client :123
// response from server : 124
沒(méi)錯(cuò)磕仅,看起來(lái)和普通Socket的用法很類似了镊讼。
性能
性能是評(píng)判一種ipc進(jìn)制好壞的重要指標(biāo),例如我們常用的Binder機(jī)制就是用了mmap機(jī)制實(shí)現(xiàn)了數(shù)據(jù)的一次拷貝提高了傳輸速度卸亮。
于是我寫了一個(gè)測(cè)試程序來(lái)對(duì)比AIDL玩裙、LocalSocket和TCP Socket的傳輸速度。測(cè)試的邏輯大概是:
- 每次傳輸讀或者寫1024 byte數(shù)據(jù)
- 計(jì)算3000次讀或者寫的耗時(shí)(也就是計(jì)算讀3000k或者寫3000k數(shù)據(jù)的總耗時(shí))
- LocalSocket和TCP Socket每次傳輸完數(shù)據(jù)都斷開連接,下次需要重新連接
在我們的產(chǎn)品設(shè)備上得到的實(shí)際數(shù)據(jù)如下:
方式 | 方向 | 第一次3000k | 第二次3000k | 第三次3000k | 第四次3000k | 平均時(shí)間 |
---|---|---|---|---|---|---|
AIDL | 讀 | 1.711s | 1.195s | 1.25s | 1.169s | 1.33125s |
LocalSocket | 讀 | 1.674s | 1.286s | 1.185s | 1.219s | 1.341s |
TCP Socket | 讀 | 10.188s | 8.926s | 8.865s | 8.803s | 9.1955s |
AIDL | 寫 | 1.261s | 1.212s | 1.175s | 1.23s | 1.2195s |
LocalSocket | 寫 | 1.387s | 1.323s | 1.23s | 1.35s | 1.3225s |
TCP Socket | 寫 | 8.284s | 8.242s | 8.324s | 8.285s | 8.28375s |
從上面的數(shù)據(jù)可以看出來(lái)LocalSocket雖然會(huì)比AIDL慢但是也差的不多溶诞,而tcp的耗時(shí)就比較多了决侈。雖然我沒(méi)有具體看過(guò)LocalSocket的底層原理,但是想來(lái)既然它在framework層被頻繁使用,那么谷歌應(yīng)該也應(yīng)該會(huì)考慮到性能這一點(diǎn)功茴。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 可以進(jìn)行數(shù)據(jù)流讀寫,沒(méi)有大小限制
- 比TCP Socket會(huì)更加安全孽亲,因?yàn)椴荒芡ㄟ^(guò)抓包監(jiān)聽(tīng)傳輸?shù)臄?shù)據(jù)
- 不會(huì)開啟線程池(Zygote之所以使用它而不是Binder也是因?yàn)锽inder機(jī)制默認(rèn)會(huì)啟動(dòng)線程池,而fork在多線程下只會(huì)fork出當(dāng)前線程)
缺點(diǎn):
- 比Binder的速度還是會(huì)稍微慢那么一點(diǎn)點(diǎn)
- 沒(méi)有像AIDL這樣的高層封裝玲昧,需要自己實(shí)現(xiàn)
- 和TCP Socket對(duì)比起來(lái)不能跨主機(jī)通信
需要注意的地方
- 雖然不是真正的網(wǎng)絡(luò)傳輸,但是也需要聲明android.permission.INTERNET權(quán)限孵延,要不然同樣會(huì)報(bào)java.net.SocketException: Permission denied異常
- 雖然可以通過(guò)LocalSocket和framework層直接通信亲配,但是如果系統(tǒng)打開了SeLinux就會(huì)出現(xiàn)Permission denied異常
- 在模擬器上LocalSocket的flush用多了耗時(shí)有時(shí)候會(huì)比較嚴(yán)重(Tcp沒(méi)有問(wèn)題,實(shí)機(jī)測(cè)試LocalSocket也沒(méi)有出現(xiàn)問(wèn)題弃榨,猜測(cè)和系統(tǒng)相關(guān))