關于Android應用與Framework的socket通信,相信關心這個問題的朋友們已經看過《android使用socket使底層和framework通信》這篇文章,美中不足的是作者隻貼出一些關鍵的代碼片段而并沒有放出源碼。我這裡還是以一個能實際運作的例子為基礎來講,這樣也友善大家學習。
首先看一下效果,如下圖。我填寫姓名"Potter",選擇性别"Mr"然後點選發送,底層socket收到消息後将消息直接傳回給我,我将傳回的結果(Mr.Potter)直接顯示在Result。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZwpmLxQzN08VNyYjM5kjMzMTMvwVOy8CXzAjMxAjMvw1ckF2bsBXdvwFdl5mLuR2cj5Set1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
ok,有了初步了解後我們現在來看一下如何一步步實作這個例子。
1、配置init.rc。我在init.rc加入如下配置
service htfskservice /system/bin/htfsk
socket htfsk stream 666 system system
oneshot
這裡配置了一個名為 “htfskservice” 的服務,手機開機後該服務會自啟動并運作/system/bin目錄下的腳本htfsk(步驟二将提到如何生成這個腳本)。
同時這裡還配置了一個名為 "htfsk" 的socket,并且隻有擁有system權限的應用才允許連接配接這個socket,如何使應用擷取system權限可以參考http://my.unix-center.net/~Simon_fu/?p=531這篇文章。
2、編寫socket服務端代碼,生成可執行腳本htfsk。
首先來看下socket服務端代碼 htfsk.c ,内容如下:
#define SOCKET_NAME "htfsk"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <cutils/sockets.h>
#include <utils/Log.h>
#include <android/log.h>
int main(){
char log[200];
int connect_number = 6;
int fdListen = -1, new_fd = -1;
int ret;
struct sockaddr_un peeraddr;
socklen_t socklen = sizeof (peeraddr);
int numbytes ;
char buff[256];
//這一步很關鍵,就是擷取init.rc中配置的名為 "htfsk" 的socket
fdListen = android_get_control_socket(SOCKET_NAME);
if (fdListen < 0) {
sprintf(log,"Failed to get socket '" SOCKET_NAME "' errno:%d", errno);
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);
exit(-1);
}
//開始監聽
ret = listen(fdListen, connect_number);
sprintf(log,"Listen result %d",ret);
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);
if (ret < 0) {
perror("listen");
exit(-1);
}
//等待Socket用戶端發啟連接配接請求
new_fd = accept(fdListen, (struct sockaddr *) &peeraddr, &socklen);
sprintf(log,"Accept_fd %d",new_fd);
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);
if (new_fd < 0 ) {
sprintf(log,"%d",errno);
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);
perror("accept error");
exit(-1);
}
while(1){
//循環等待Socket用戶端發來消息
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI","Waiting for receive");
if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1){
sprintf(log,"%d",errno);
__android_log_write(ANDROID_LOG_DEBUG,"FTM_JNI",log);
perror("recv");
continue;
}
//發送消息回執給Socket用戶端
if(send(new_fd,buff,strlen(buff),0)==-1)
{
perror("send");
close(new_fd);
exit(0);
}
}
close(new_fd);
close(fdListen);
return 0;
}
寫好服務端代碼後我們要将他編譯成可執行腳本htfsk,編譯的Android.mk内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS :=optional
LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
LOCAL_SHARED_LIBRARIES := libcutils liblog
LOCAL_MODULE:= htfsk
LOCAL_SRC_FILES:=htfsk.c
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)
編譯成功後就會在/system/bin/目錄下找到生成的可執行腳本htfsk
3、編寫用戶端java代碼。核心代碼如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;
/**
* Socket用戶端
*
* @author lai_zs
* @date:2012-3-17 下午12:15:09
*/
public class SocketClient {
private final String SOCKET_NAME = "htfsk";
private LocalSocket client;
private LocalSocketAddress address;
private boolean isConnected = false;
private int connetTime = 1;
public SocketClient() {
client = new LocalSocket();
address = new LocalSocketAddress(SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED);
new ConnectSocketThread().start();
}
/**
* 發送消息
* @param msg
* @return 傳回Socket服務端的消息回執
*/
public String sendMsg(String msg) {
if (!isConnected) {
return "Connect fail";
}
try {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(client.getOutputStream());
out.println(msg);
out.flush();
return in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "Nothing return";
}
/**
* 異步連接配接Socket,如果連接配接不上會嘗試重複連接配接十次
*
* @author Administrator
*
*/
private class ConnectSocketThread extends Thread {
@Override
public void run() {
while (!isConnected && connetTime <= 10) {
try {
sleep(1000);
Log.i("SocketClient","Try to connect socket;ConnectTime:"+connetTime);
client.connect(address);
isConnected = true;
} catch (Exception e) {
connetTime++;
isConnected = false;
Log.i("SocketClient","Connect fail");
}
}
}
}
/**
* 關閉Socket
*/
public void closeSocket() {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
總結:經過以上三個步驟本執行個體就基本完成了,源碼工程MySocket我已經上傳至http://download.csdn.net/detail/goleftgoright/4183596
工程MySocket使用步驟:
1、配置init.rc。
2、将工程MySocket拷貝到android源碼的/packages/apps目錄下編譯就可以了。
編譯成功後就可以刷機了,手機開機啟動後可以看到 /dev/socket 目錄下服務端Socket已經成功啟動了,如下圖:
再來試一下,