• Android利用LocalSocket实现Java端进程与C端进程之间的IPC


      Android是建立在Linux之上的OS,在涉及到安全、网络协议、文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需要打通Java端进程和C端进程,使之能高效地通信。这样,C端进程用于实现功能,Java端进程负责UI、功能的触发及结果处理就可以了。

      对于*nix系统来说,“一切皆为文件”,Socket也不例外,Socket按照收发双方的媒介来说有三种类型:1,通过网络端口;2,通过文件系统;3,通过内存映射文件。具体说来,三种类型均可以用来作为IPC的Socket:1,通过本地回环接口(即LoopBack)127.0.0.1来收发数据;2,通过文件作为收发数据的中转站;3,在内存中开辟一块区域作为收发数据的中转站,此区域仍然使用文件读写API进行访问。LocalSocket支持方式2和方式3,从效率的角度来说,显然是方式3效率最高,那么下面我们就使用LocalSocket来演示如何实现Java端进程与C端进程之间的IPC。

      以下的demo是Java端作为server,C端作为client;实际场景中可能更多的是Java端作为client,而C端作为server。

    服务端代码如下:

      1 package main.activity;
      2 
      3 import java.io.IOException;
      4 import java.io.InputStream;
      5 import java.io.InputStreamReader;
      6 
      7 import android.app.Activity;
      8 import android.net.LocalServerSocket;
      9 import android.net.LocalSocket;
     10 import android.os.Bundle;
     11 import android.util.Log;
     12 
     13 /**
     14  * @author pengyiming
     15  * @note 启动localSocketServer
     16  *
     17  */
     18 
     19 public class LocalSocketServerActivity extends Activity
     20 {
     21     /* 数据段begin */
     22     private final String TAG = "server";
     23     
     24     private ServerSocketThread mServerSocketThread;
     25     /* 数据段end */
     26     
     27     /* 函数段begin */
     28     @Override
     29     protected void onCreate(Bundle savedInstanceState)
     30     {
     31         super.onCreate(savedInstanceState);
     32         
     33         mServerSocketThread = new ServerSocketThread();
     34         mServerSocketThread.start();
     35     }
     36     
     37     @Override
     38     protected void onDestroy()
     39     {
     40         super.onDestroy();
     41         
     42         mServerSocketThread.stopRun();
     43     }
     44     /* 函数段end */
     45     
     46     /* 内部类begin */
     47     private class ServerSocketThread extends Thread
     48     {
     49         private boolean keepRunning = true;
     50         private LocalServerSocket serverSocket;
     51         
     52         private void stopRun()
     53         {
     54             keepRunning = false;
     55         }
     56         
     57         @Override
     58         public void run()
     59         {
     60             try
     61             {
     62                 serverSocket = new LocalServerSocket("pym_local_socket");
     63             }
     64             catch (IOException e)
     65             {
     66                 e.printStackTrace();
     67                 
     68                 keepRunning = false;
     69             }
     70             
     71             while(keepRunning)
     72             {
     73                 Log.d(TAG, "wait for new client coming !");
     74                 
     75                 try
     76                 {
     77                     LocalSocket interactClientSocket = serverSocket.accept();
     78                     
     79                     //由于accept()在阻塞时,可能Activity已经finish掉了,所以再次检查keepRunning
     80                     if (keepRunning)
     81                     {
     82                         Log.d(TAG, "new client coming !");
     83                         
     84                         new InteractClientSocketThread(interactClientSocket).start();
     85                     }
     86                 }
     87                 catch (IOException e)
     88                 {
     89                     e.printStackTrace();
     90                     
     91                     keepRunning = false;
     92                 }
     93             }
     94             
     95             if (serverSocket != null)
     96             {
     97                 try
     98                 {
     99                     serverSocket.close();
    100                 }
    101                 catch (IOException e)
    102                 {
    103                     e.printStackTrace();
    104                 }
    105             }
    106         }
    107     }
    108     
    109     private class InteractClientSocketThread extends Thread
    110     {
    111         private LocalSocket interactClientSocket;
    112         
    113         public InteractClientSocketThread(LocalSocket interactClientSocket)
    114         {
    115             this.interactClientSocket = interactClientSocket;
    116         }
    117         
    118         @Override
    119         public void run()
    120         {
    121             StringBuilder recvStrBuilder = new StringBuilder();
    122             InputStream inputStream = null;
    123             try
    124             {
    125                 inputStream = interactClientSocket.getInputStream();
    126                 InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
    127                 char[] buf = new char[4096];
    128                 int readBytes = -1;
    129                 while ((readBytes = inputStreamReader.read(buf)) != -1)
    130                 {
    131                     String tempStr = new String(buf, 0, readBytes);
    132                     recvStrBuilder.append(tempStr);
    133                 }
    134             }
    135             catch (IOException e)
    136             {
    137                 e.printStackTrace();
    138                 
    139                 Log.d(TAG, "resolve data error !");
    140             }
    141             finally
    142             {
    143                 if (inputStream != null)
    144                 {
    145                     try
    146                     {
    147                         inputStream.close();
    148                     }
    149                     catch (IOException e)
    150                     {
    151                         e.printStackTrace();
    152                     }
    153                 }
    154             }
    155         }
    156     }
    157     /* 内部类end */
    158 }

     客户端代码如下:

     1 package main.activity;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.util.Log;
     6 
     7 /**
     8  * @author pengyiming
     9  * @note 用于启动localSocketClient,向server发送心跳报文
    10  *
    11  */
    12 
    13 public class LocalSocketClientActivity extends Activity
    14 {
    15     /* 数据段begin */
    16     private final String TAG = "client";
    17     
    18     private HeartBeatThread mHeartBeatThread;
    19     
    20     public native int startHeartBeat();
    21     /* 数据段end */
    22     
    23     /* 函数段begin */
    24     static
    25     {
    26         System.loadLibrary("pymclient");
    27     }
    28     
    29     @Override
    30     protected void onCreate(Bundle savedInstanceState)
    31     {
    32         super.onCreate(savedInstanceState);
    33         
    34         mHeartBeatThread = new HeartBeatThread();
    35         mHeartBeatThread.start();
    36     }
    37     
    38     @Override
    39     protected void onDestroy()
    40     {
    41         super.onDestroy();
    42         
    43         mHeartBeatThread.stopRun();
    44     }
    45     /* 函数段end */
    46     
    47     /* 内部类begin */
    48     private class HeartBeatThread extends Thread
    49     {
    50         int ret;
    51         boolean keepRunning = true;
    52         
    53         public void stopRun()
    54         {
    55             keepRunning = false;
    56         }
    57         
    58         @Override
    59         public void run()
    60         {
    61             Log.d(TAG, "start heart beat!");
    62             
    63             while (keepRunning)
    64             {
    65                 ret = startHeartBeat();
    66                 
    67                 Log.d(TAG, "ret = " + ret);
    68                 
    69                 if (ret != 0)
    70                 {
    71                     break;
    72                 }
    73                 
    74                 try
    75                 {
    76                     sleep(1000);
    77                 }
    78                 catch (InterruptedException e)
    79                 {
    80                     e.printStackTrace();
    81                 }
    82             }
    83             
    84             Log.d(TAG, "stop heart beat!");
    85         }
    86     }
    87     /* 内部类end */
    88 }

    上述客户端代码启动了一个线程用于发送“心跳”报文,每隔1s构建一个新的LocalSocket,连接服务端并发送流数据,其核心就在于native方法的实现。值得一提的是,我最初使用原生socket函数,没想connect总是返回错误;后来在同事的提醒下,我参考了Android源码rild.c中socket_local_client的使用,并从socket_local_client.c中抽取出相应代码改写而成。

    客户端native方法头文件:

     1 /* DO NOT EDIT THIS FILE - it is machine generated */
     2 #include <jni.h>
     3 /* Header for class main_activity_LocalSocketClientActivity */
     4 
     5 #ifndef _Included_main_activity_LocalSocketClientActivity
     6 #define _Included_main_activity_LocalSocketClientActivity
     7 #ifdef __cplusplus
     8 extern "C" {
     9 #endif
    10 
    11 /* socket命名空间(见cutils/sockets.h) */
    12 #define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
    13 #define ANDROID_SOCKET_NAMESPACE_RESERVED 1
    14 #define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
    15 
    16 /* socket类型 */
    17 #define SOCK_STREAM      1
    18 #define SOCK_DGRAM       2
    19 #define SOCK_RAW         3
    20 #define SOCK_RDM         4
    21 #define SOCK_SEQPACKET   5
    22 #define SOCK_PACKET      10
    23 
    24 /* 清0宏 */
    25 #define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
    26 
    27 /* 错误码定义 */
    28 #define NO_ERR 0
    29 #define CREATE_ERR -1
    30 #define CONNECT_ERR -2
    31 #define LINUX_MAKE_ADDRUN_ERROR -3
    32 #define NO_LINUX_MAKE_ADDRUN_ERROR -4
    33 #define CLOSE_ERR -5
    34 
    35 /* 是否使用linux的本地socket命令空间 */
    36 #define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE "linux_local_socket_namespace"
    37 
    38 #undef main_activity_LocalSocketClientActivity_MODE_PRIVATE
    39 #define main_activity_LocalSocketClientActivity_MODE_PRIVATE 0L
    40 #undef main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE
    41 #define main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE 1L
    42 #undef main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE
    43 #define main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE 2L
    44 #undef main_activity_LocalSocketClientActivity_MODE_APPEND
    45 #define main_activity_LocalSocketClientActivity_MODE_APPEND 32768L
    46 #undef main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS
    47 #define main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS 4L
    48 #undef main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE
    49 #define main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE 1L
    50 #undef main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND
    51 #define main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND 2L
    52 #undef main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND
    53 #define main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND 4L
    54 #undef main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT
    55 #define main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT 8L
    56 #undef main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT
    57 #define main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
    58 #undef main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY
    59 #define main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY 32L
    60 #undef main_activity_LocalSocketClientActivity_BIND_IMPORTANT
    61 #define main_activity_LocalSocketClientActivity_BIND_IMPORTANT 64L
    62 #undef main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY
    63 #define main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY 128L
    64 #undef main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE
    65 #define main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE 1L
    66 #undef main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY
    67 #define main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY 2L
    68 #undef main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED
    69 #define main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED 4L
    70 #undef main_activity_LocalSocketClientActivity_RESULT_CANCELED
    71 #define main_activity_LocalSocketClientActivity_RESULT_CANCELED 0L
    72 #undef main_activity_LocalSocketClientActivity_RESULT_OK
    73 #define main_activity_LocalSocketClientActivity_RESULT_OK -1L
    74 #undef main_activity_LocalSocketClientActivity_RESULT_FIRST_USER
    75 #define main_activity_LocalSocketClientActivity_RESULT_FIRST_USER 1L
    76 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE
    77 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE 0L
    78 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER
    79 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER 1L
    80 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT
    81 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT 2L
    82 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL
    83 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
    84 #undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL
    85 #define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
    86 /*
    87  * Class:     main_activity_LocalSocketClientActivity
    88  * Method:    startHeartBeat
    89  * Signature: ()I
    90  */
    91 JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat
    92   (JNIEnv *, jobject);
    93 
    94 #ifdef __cplusplus
    95 }
    96 #endif
    97 #endif

    客户端native方法实现:

      1 /* 头文件begin */
      2 #include "main_activity_LocalSocketClientActivity.h"
      3 
      4 #include <sys/socket.h>
      5 #include <sys/un.h>
      6 #include <stddef.h>
      7 #include <string.h>
      8 /* 头文件end */
      9 
     10 #ifdef __cplusplus
     11 extern "C" {
     12 #endif
     13 
     14 /*
     15  * Class:     main_activity_LocalSocketClientActivity
     16  * Method:    startHeartBeat
     17  */
     18 JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat(JNIEnv * env, jobject object)
     19 {
     20     int socketID;
     21     struct sockaddr_un serverAddr;
     22     char path[] = "pym_local_socket";
     23     int ret;
     24 
     25     socketID = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
     26     if (socketID < 0)
     27     {
     28         return socketID;
     29     }
     30 
     31     ret = close(socketID);
     32     if (ret < 0)
     33     {
     34         return CLOSE_ERR;
     35     }
     36 
     37     return NO_ERR;
     38 }
     39 
     40 /* 创建本地socket客户端 */
     41 int socket_local_client(const char *name, int namespaceId, int type)
     42 {
     43     int socketID;
     44     int ret;
     45 
     46     socketID = socket(AF_LOCAL, type, 0);
     47     if(socketID < 0)
     48     {
     49         return CREATE_ERR;
     50     }
     51 
     52     ret = socket_local_client_connect(socketID, name, namespaceId, type);
     53     if (ret < 0)
     54     {
     55         close(socketID);
     56 
     57         return ret;
     58     }
     59 
     60     return socketID;
     61 }
     62 
     63 /* 连接到相应的fileDescriptor上 */
     64 int socket_local_client_connect(int fd, const char *name, int namespaceId, int type)
     65 {
     66     struct sockaddr_un addr;
     67     socklen_t socklen;
     68     size_t namelen;
     69     int ret;
     70 
     71     ret = socket_make_sockaddr_un(name, namespaceId, &addr, &socklen);
     72     if (ret < 0)
     73     {
     74         return ret;
     75     }
     76 
     77     if(connect(fd, (struct sockaddr *) &addr, socklen) < 0)
     78     {
     79         return CONNECT_ERR;
     80     }
     81 
     82     return fd;
     83 }
     84 
     85 /* 构造sockaddr_un */
     86 int socket_make_sockaddr_un(const char *name, int namespaceId, struct sockaddr_un *p_addr, socklen_t *socklen)
     87 {
     88     size_t namelen;
     89 
     90     MEM_ZERO(p_addr, sizeof(*p_addr));
     91 #ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
     92 
     93     namelen  = strlen(name);
     94 
     95     // Test with length +1 for the *initial* ''.
     96     if ((namelen + 1) > sizeof(p_addr->sun_path))
     97     {
     98         return LINUX_MAKE_ADDRUN_ERROR;
     99     }
    100     p_addr->sun_path[0] = 0;
    101     memcpy(p_addr->sun_path + 1, name, namelen);
    102 
    103 #else
    104 
    105     namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
    106 
    107     /* unix_path_max appears to be missing on linux */
    108     if (namelen > (sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1))
    109     {
    110         return NO_LINUX_MAKE_ADDRUN_ERROR;
    111     }
    112 
    113     strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
    114     strcat(p_addr->sun_path, name);
    115 
    116 #endif
    117 
    118     p_addr->sun_family = AF_LOCAL;
    119     *socklen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
    120 
    121     return NO_ERR;
    122 }
    123 
    124 #ifdef __cplusplus
    125 }
    126 #endif

    注意到100~101行比较特殊,是从p_addr->sun_path[1]开始拷贝本地域名,这就是之前为什么一直connect不上的原因,至于为什么偏移1个字节来拷贝本地域名,你可以在*nix系统下输入"man 7 unix"来找到原因。

      先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! 在close之前加入这段代码吧~

    1 int ret;
    2 char buf[] = "hello";
    3 
    4 ret = write(socketID, buf, strlen(buf));
  • 相关阅读:
    eclipse快捷键
    go 中 var声明对比
    Post 中 Body 的 ContentType 用 Postman 举例
    MongoDB随笔(二) mongorestore恢复数据库
    MongoDB随笔(零) mongod配置 ...不断完善...会变得很长很长很长……
    MongoDB随笔(一)mac OSX下brew安装MongoDB
    mac OSX的 brew软件包管理器 相当于 centos下的yum
    2021-01-27 解决mac使用brew update更新无反应的问题(切换git地址)
    Ruby中实现module继承
    redmine问题集锦
  • 原文地址:https://www.cnblogs.com/zealotrouge/p/3152941.html
Copyright © 2020-2023  润新知