Android : App經過LocalSocket 與 HAL間通訊

  LocalSocket其通訊方式與Socket差很少,只是LocalSocket沒有跨越網絡邊界。對於*nix系統來講,「一切皆爲文件」,Socket也不例外,Socket按照收發雙方的媒介來講有三種類型:html

  1,經過網絡端口: 即經過本地迴環接口(即LoopBack)127.0.0.1來收發數據;java

  2,經過文件系統: 經過文件做爲收發數據的中轉站;linux

  3,經過內存映射文件:在內存中開闢一塊區域做爲收發數據的中轉站,此區域仍然使用文件讀寫API進行訪問;android

  LocalSocket支持方式2和方式3。ios

 

如下經過HAL層(c)做爲server,App端(java)做爲client,進行LocalSocket通訊演示(核心部分代碼):服務器

C代碼:網絡

J_U8* sName="nano_server_socket"; J_Int server_sockfd, client_sockfd; J_Int server_len, client_len; J_Int reuse = 1; J_Int err;   struct sockaddr_un server_address; /*聲明一個UNIX域套接字結構*/  
    struct sockaddr_un client_address; unlink (sName); /*刪除原有server_socket對象*/

    /*建立 socket, 通訊協議爲AF_LOCAL, SOCK_STREAM 數據方式*/ server_sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); if(server_sockfd < 0){ ALOGE("server_sockfd error : %s\n",strerror(errno)); return; } /*配置服務器信息(socket對象路徑)*/
    // Check with length +1 for the *initial* '\0'.
    if ((strlen(sName) + 1) > sizeof(server_address.sun_path)) { ALOGE("name too long\n"); goto EXIT; } /* * Note: The path in this case is *not* supposed to be * '\0'-terminated. ("man 7 unix" for the gory details.) */ server_address.sun_path[0] = 0; memcpy(server_address.sun_path + 1, sName, strlen(sName)); /*配置服務器信息(通訊協議)*/ server_address.sun_family = AF_LOCAL; /*默認設置resue FLAG*/
     if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { ALOGE("reuse error\n"); goto EXIT; } /*配置服務器信息(服務器地址長度)*/ client_len = server_len = strlen(sName) + offsetof(struct sockaddr_un, sun_path) + 1; /*綁定 socket 對象*/ bind (server_sockfd, (struct sockaddr *)&server_address, server_len); /*監聽網絡,隊列數爲1*/  
    if(listen(server_sockfd,1)<0){ ALOGE("listen error : %s\n",strerror(errno)); goto EXIT; }
 /*接受客戶端請求; 第2個參數用來存儲客戶端地址; 第3個參數用來存儲客戶端地址的大小*/ /*創建(返回)一個到客戶端的文件描述符,用以對客戶端的讀寫操做*/ client_sockfd = accept (server_sockfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len);
   if (client_sockfd == -1) { ALOGE("accept error : %s\n",strerror(errno)); goto EXIT; } else if (client_sockfd> FD_SETSIZE){ ALOGE("accept reach max\n"); close(client_sockfd); goto EXIT; }
   /*start read loop*/ 
   while(1) {

      char buf[1024]={0};
      ssize_t res=read(client_socketfd,buf,sizeof(buf));app

      ALOGD("server get data : %s", buf);socket

      if(res <=0){
        if(errno == EAGAIN)
          continue;
        ALOGE("errno %s\n",strerror(errno));
        close(client_socketfd);
        return;
       }ide

    }

EXIT: close(server_sockfd);

HAL層可具體參考Android源碼的 system\bt\osi\src\socket_utils\ 目錄下的 socket_local_client.cc 和 socket_local_server.cc 代碼(Android 8.0),

直接調用以下封裝好的接口便可:

/***********/
/***服務端***/
/**********/
/*(1)建立server socket接收client端數據*/ server_sockfd = osi_socket_local_server("server_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT,SOCK_STREAM); /*(2)等待client端鏈接*/ client_sockfd = accept (server_sockfd, NULL, NULL); /*(3)數據讀取線程*/
while(1){ ssize_t res=read(client_sockfd ,buf,sizeof(buf)); } /***********/
/***客戶端***/
/**********/
/*(1)建立client socket */ client_sockfd = osi_socket_local_client("client_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); /*(2)發送數據到server端 */ write(client_sockfd, "hello", strlen("hello"));

 

 

JAVA代碼:

package com.example.administrator.localsocket; import android.net.LocalServerSocket; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.Bundle; import android.os.RemoteException; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; import java.io.IOException; import static android.widget.Toast.LENGTH_SHORT; public class MainActivity extends AppCompatActivity { private static final String SOCKET_ADDRESS = "nano_server_socket"; //和HAL層統一地址名稱  LocalSocket client_socket = null; LocalSocketAddress addr; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); 
        client_socket = new LocalSocket(); //建立socket對象 addr = new LocalSocketAddress(SOCKET_ADDRESS,LocalSocketAddress.Namespace.ABSTRACT); //使用虛空間地址 try { client_socket.connect(addr);  //請求鏈接 } catch (IOException e) { e.printStackTrace(); } findViewById(R.id.sendMsg).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //發送數據
                    String data="hello"; client_socket.getOutputStream().write(data.getBytes()); } catch (IOException e) { e.printStackTrace(); } } });    }
@Override
protected void onStop() { super.onStop(); try { client_socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

:Android8.0版本驗證app端要用LocalSocket,須要兩個條件:

   1.apk須要簽名成platform_app; (方法可參考: https://www.cnblogs.com/blogs-of-lxl/p/9233285.html )

   2.須要給platform_app增長selinux權限,修改xxx\sepolicy\platform_app.te,添加以下兩條規則:

    typeattribute platform_app mlstrustedsubject;
    allow platform_app audioserver:unix_stream_socket connectto;

 

測試結果:

相關文章
相關標籤/搜索