Android 使用Socket完成程式間通訊

jia635發表於2014-08-06


看一些Android應用,特別是涉及到底層的功能性的應用,比如遊戲加速,修改記憶體,掛機指令碼神馬的,發現裡面的通訊機制無一例外的都是使用的socket,基本上已經成為這類應用的一種標配了。

因為這類應用有以下的幾個共同點:


1 需要android 手機的root許可權,畢竟要修改一些比較底層的東西,沒有root許可權有時候木有辦法修改啊


2 有自己的so,同時比較重要的或者比較吃力的活都編譯成一個可執行的elf檔案,然後讓apk應用給這個elf檔案給啟動起來。關於這個elf檔案放置的位置也有幾種選擇。


情況1:直接放在應用包下面的lib目錄下,然後給這個可執行檔案賦予一個可執行的許可權。


情況2:放在assets資料夾下面,在程式啟動的時候拷貝到files資料夾下面,賦予可執行的許可權後,然後執行


情況3:也是放在assets資料夾下面,只不過安全性高一點,一般會做一個簡單的異或或者移位加密,然後修改以下副檔名,拷貝過去的時候減密一下。再執行,有時候執行了以後會把真正的elf檔案再給刪除了。


3 可執行的so檔案和java應用程式之間的通訊使用socket,因為畢竟可執行的so才是真正幹活的主力,而java應用程式要給so傳送指令,而so也要給java應用程式反饋一些有用的資料供上層顯示給使用者。關於socket通訊的方式也有下面的幾種情況


情況1: 可執行的so作為server端,java應用程式給其傳送指令的時候主動去連結可執行so,傳送資料,有時候so也會返回一些資料,一般這種情況so就直接把活給幹了,比如遊戲加減速。


情況2:可執行的so檔案作為客戶端,同時使用長連結的方式,這種情況就需要java應用程式作為server端了,這裡的稍微巧妙一點的是不採用固定的socket的埠號,而是java應用程式在建立socket的時候使用系統自動分配的埠號。不過需要通過java應用程式在啟動可執行so的時候將埠號傳遞給so檔案。


原理基本上就這麼多了,下面的是一個例子,這個例子涉及到的程式設計技術主要是java應用程式自己建立socket的客戶端和伺服器端,然後進行通訊,同時也啟動了一個so的可執行檔案,通過程式之間的資料流,向可執行的so傳送資料,可執行的so將接收到的資料通過log給列印出來。


進入demo例子,


點選啟動 socket server


這個時候log會列印出來


11-04 15:56:28.341:V/cheatecore-server(5774): server port = 44034


這樣的資料,


由於serverSocket = new ServerSocket(m_port);


其中 m_port 為0,所以埠號是系統臨時分配的。


然後socketserver 就開始等待有客戶端的socket 來連結


m_socket = serverSocket.accept();


其中 accept 是阻塞函式,會將這個執行緒阻塞掉的。這個時候


通過 adb shell 進入android手機,使用 netstat –an | busybox grep 44034 檢視,發現是這樣的現象


顯然這個埠是 listen 狀態


然後點選設定客戶端socket埠號和啟動客戶端socket


11-04 16:03:11.067: V/cheatecore(5774): 獲取伺服器端的埠號:44034
11-04 16:03:15.575:V/cheatecore-client(5774): client socket begin
11-04 16:03:15.575:V/cheatecore-server(5774): a client socket come here!!!


這個時候出現了a client socket comehere!!!
這條log再次執行剛才的
netstat –an | busybox grep 44034
發現已經變成如下的結果了



已經有隨機分配的客戶端埠號跟server端建立了連結了


然後再點選獲取資料流


reader = new BufferedReader(inputreader);
serveroutwriter = new PrintWriter(newBufferedWriter(new OutputStreamWriter(sk.getOutputStream())),true);


主要獲取socket的輸入流和輸出流,也就是客戶端和服務端的tcp連結已經建立起來了,剩下的就是通過流來接收和傳送資料了


接著點選
啟動伺服器socket 接收資料執行緒


String sReader = reader.readLine();
這條語句就在等著客戶端的socket資料過來呢,其中readLine是一個阻塞函式,熟悉c語言的同學,其實這個就是socket 中的 recv函式,阻塞呼叫了。




因為tcp是全雙工的,並且當前狀態是長連結,所以服務端可以向客戶端傳送資料,客戶端也可以向服務端傳送資料,下一個按鈕


伺服器端socket傳送資料


11-04 16:11:19.810: V/cheatecore-client(5774):客戶端接收到服務端的資料:server data
11-04 16:11:19.810:V/cheatecore-server(5774): 服務端接收到客戶端的資料:clientdata


這個時候出現的log,客戶端在收到伺服器的資料以後,也會向伺服器端再返回資料,這樣就完成了一次互動,但是其實這個時候socket並沒有斷掉,因為是長連結,可以進行多次傳送


最後結束socket


關閉了socket,這個時候再用 netstat –an | busybox grep 44034
這個指令來進行查詢,發現如下圖:



剛才建立的socket已經釋放了,再過一會查詢wait超時後也會將埠釋放掉了


測試向啟動的程式傳送資料


11-04 16:15:11.278: E/cheatecore(6163):input target main function!!!!
11-04 16:15:11.278: E/cheatecore(6163):scanf input content is 44034


主要過程是首先啟動


/data/data/com.example.sockettest/files/target
這個程式


testprocee =Runtime.getRuntime().exec(cmd);
PrintWriterpoutwriter = newPrintWriter(newBufferedWriter(newOutputStreamWriter(testprocee.getOutputStream())),true); 
poutwriter.println(test);


然後傳送資料,完成了這次程式間資料的互動,其實這也是程式之間通訊的一種方式,和利用socket完成程式之間的通訊並列的。

相關文章