LocalSocket作爲安卓提供的一種IPC機制,可能應用層的同學比較陌生,我實際也是在這段時間做項目使用到才注意到它並去了解的。不過實際上framework層裏面被頻繁使用到了,例如我很久前寫的博客從源碼看安卓應用的啓動過程裏面提到其他進程和Zygote進程之間的通信使用的是LocalSocket。
那麼LocalSocket和Socket到底有什麼不同呢?官方文檔裏面其實提到了它其實是基於UNIX-domain socket的:
Non-standard class for creating an inbound UNIX-domain socket in the Linux abstract namespace.
Socket本來是用來做不同主機間的網絡通信的,如果有人想拿來做本機的IPC通信就會發現它的性能堪憂(例如實現binder機制做不到的傳輸大文件),因爲它需要走網絡協議棧、打包拆包、計算校驗等,如果是TCP還需要走三次握手和應答。
於是後面就發展出了UNIX-domain socket (LocalSocket),它的api和socket的基本一致,但是本質上只是一種IPC通信,不可和外部主機通信,但是因爲IPC通信是可靠通信,直接將數據拷貝到目標進程內存即可,所以沒有之前說的那些耗時的操作。
使用方式
我們先來看看它的使用方式:
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
沒錯,看起來和普通Socket的用法很類似了。
性能
性能是評判一種ipc進制好壞的重要指標,例如我們常用的Binder機制就是用了mmap機制實現了數據的一次拷貝提高了傳輸速度。
於是我寫了一個測試程序來對比AIDL、LocalSocket和TCP Socket的傳輸速度。測試的邏輯大概是:
- 每次傳輸讀或者寫1024 byte數據
- 計算3000次讀或者寫的耗時(也就是計算讀3000k或者寫3000k數據的總耗時)
- LocalSocket和TCP Socket每次傳輸完數據都斷開連接,下次需要重新連接
在我們的產品設備上得到的實際數據如下:
方式 | 方向 | 第一次3000k | 第二次3000k | 第三次3000k | 第四次3000k | 平均時間 |
---|---|---|---|---|---|---|
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 |
從上面的數據可以看出來LocalSocket雖然會比AIDL慢但是也差的不多,而tcp的耗時就比較多了。雖然我沒有具體看過LocalSocket的底層原理,但是想來既然它在framework層被頻繁使用,那麼谷歌應該也應該會考慮到性能這一點。
優缺點
優點:
- 可以進行數據流讀寫,沒有大小限制
- 比TCP Socket會更加安全,因爲不能通過抓包監聽傳輸的數據
- 不會開啓線程池(Zygote之所以使用它而不是Binder也是因爲Binder機制默認會啓動線程池,而fork在多線程下只會fork出當前線程)
缺點:
- 比Binder的速度還是會稍微慢那麼一點點
- 沒有像AIDL這樣的高層封裝,需要自己實現
- 和TCP Socket對比起來不能跨主機通信
需要注意的地方
- 雖然不是真正的網絡傳輸,但是也需要聲明android.permission.INTERNET權限,要不然同樣會報java.net.SocketException: Permission denied異常
- 雖然可以通過LocalSocket和framework層直接通信,但是如果系統打開了SeLinux就會出現Permission denied異常
- 在模擬器上LocalSocket的flush用多了耗時有時候會比較嚴重(Tcp沒有問題,實機測試LocalSocket也沒有出現問題,猜測和系統相關)