飘云阁(PYG官方论坛)

 找回密码
 加入论坛

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 4814|回复: 5

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

[复制链接]

该用户从未签到

发表于 2014-11-27 12:14:29 | 显示全部楼层 |阅读模式
本帖最后由 空道 于 2014-11-27 13:58 编辑

Android是建立在Linux之上的OS,在涉及到安全、网络协议、文件加密等功能时,往往需要通过C语言调用底层API来实现,而如何发出指令让C端执行我们想要的功能,并且在执行之后有返回结果呢,这就需要打通Java端进程和C端进程,使之能高效地通信。这样,C端进程用于实现功能,Java端进程负责UI、功能的触发及结果处理就可以了。
  对于*nix系统来说,一切皆为文件Socket也不例外,Socket按照收发双方的媒介来说有三种类型:1,通过网络端口;2,通过文件系统;3,通过内存映射文件。具体说来,三种类型均可以用来作为IPCSocket1,通过本地回环接口(LoopBack)127.0.0.1来收发数据;2,通过文件作为收发数据的中转站;3,在内存中开辟一块区域作为收发数据的中转站,此区域仍然使用文件读写API进行访问。LocalSocket支持方式2和方式3,从效率的角度来说,显然是方式3效率最高,那么下面我们就使用LocalSocket来演示如何实现Java端进程与C端进程之间的IPC
  以下的demoJava端作为serverC端作为client;实际场景中可能更多的是Java端作为client,而C端作为server
服务端代码如下:
package main.activity;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.app.Activity;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Bundle;
import android.util.Log;
/**
* @author pengyiming
* @note 启动localSocketServer
*
*/
public class LocalSocketServerActivity extends Activity
{
    /* 数据段begin */
    private final String TAG = "server";
   
    private ServerSocketThread mServerSocketThread;
    /* 数据段end */
   
    /* 函数段begin */
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        mServerSocketThread = new ServerSocketThread();
        mServerSocketThread.start();
    }
   
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        
        mServerSocketThread.stopRun();
    }
    /* 函数段end */
   
    /* 内部类begin */
    private class ServerSocketThread extends Thread
    {
        private boolean keepRunning = true;
        private LocalServerSocket serverSocket;
        
        private void stopRun()
        {
            keepRunning = false;
        }
        
        @Override
        public void run()
        {
            try
            {
                serverSocket = new LocalServerSocket("pym_local_socket");
            }
            catch (IOException e)
            {
                e.printStackTrace();
               
                keepRunning = false;
            }
            
            while(keepRunning)
            {
                Log.d(TAG, "wait for new client coming !");
               
                try
                {
                    LocalSocket interactClientSocket = serverSocket.accept();
                    
                    //由于accept()在阻塞时,可能Activity已经finish掉了,所以再次检查keepRunning
                    if (keepRunning)
                    {
                        Log.d(TAG, "new client coming !");
                        
                        new InteractClientSocketThread(interactClientSocket).start();
                    }
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                    
                    keepRunning = false;
                }
            }
            
            if (serverSocket != null)
            {
                try
                {
                    serverSocket.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
   
    private class InteractClientSocketThread extends Thread
    {
        private LocalSocket interactClientSocket;
        
        public InteractClientSocketThread(LocalSocket interactClientSocket)
        {
            this.interactClientSocket = interactClientSocket;
        }
        
        @Override
        public void run()
        {
            StringBuilder recvStrBuilder = new StringBuilder();
            InputStream inputStream = null;
            try
            {
                inputStream = interactClientSocket.getInputStream();
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                char[] buf = new char[4096];
                int readBytes = -1;
                while ((readBytes = inputStreamReader.read(buf)) != -1)
                {
                    String tempStr = new String(buf, 0, readBytes);
                    recvStrBuilder.append(tempStr);
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
               
                Log.d(TAG, "resolve data error !");
            }
            finally
            {
                if (inputStream != null)
                {
                    try
                    {
                        inputStream.close();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    /* 内部类end */
}
客户端代码如下:
package main.activity;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
/**
* @author pengyiming
* @note 用于启动localSocketClient,向server发送心跳报文
*
*/
public class LocalSocketClientActivity extends Activity
{
    /* 数据段begin */
    private final String TAG = "client";
   
    private HeartBeatThread mHeartBeatThread;
   
    public native int startHeartBeat();
    /* 数据段end */
   
    /* 函数段begin */
    static
    {
        System.loadLibrary("pymclient");
    }
   
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        mHeartBeatThread = new HeartBeatThread();
        mHeartBeatThread.start();
    }
   
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        
        mHeartBeatThread.stopRun();
    }
    /* 函数段end */
   
    /* 内部类begin */
    private class HeartBeatThread extends Thread
    {
        int ret;
        boolean keepRunning = true;
        
        public void stopRun()
        {
            keepRunning = false;
        }
        
        @Override
        public void run()
        {
            Log.d(TAG, "start heart beat!");
            
            while (keepRunning)
            {
                ret = startHeartBeat();
               
                Log.d(TAG, "ret = " + ret);
               
                if (ret != 0)
                {
                    break;
                }
               
                try
                {
                    sleep(1000);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            
            Log.d(TAG, "stop heart beat!");
        }
    }
    /* 内部类end */
}
上述客户端代码启动了一个线程用于发送心跳报文,每隔1s构建一个新的LocalSocket,连接服务端并发送流数据,其核心就在于native方法的实现。值得一提的是,我最初使用原生socket函数,没想connect总是返回错误;后来在同事的提醒下,我参考了Android源码rild.csocket_local_client的使用,并从socket_local_client.c中抽取出相应代码改写而成。
客户端native方法头文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class main_activity_LocalSocketClientActivity */
#ifndef _Included_main_activity_LocalSocketClientActivity
#define _Included_main_activity_LocalSocketClientActivity
#ifdef __cplusplus
extern "C" {
#endif
/* socket命名空间(cutils/sockets.h) */
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2
/* socket类型 */
#define SOCK_STREAM      1
#define SOCK_DGRAM       2
#define SOCK_RAW         3
#define SOCK_RDM         4
#define SOCK_SEQPACKET   5
#define SOCK_PACKET      10
/* 0*/
#define MEM_ZERO(pDest, destSize) memset(pDest, 0, destSize)
/* 错误码定义 */
#define NO_ERR 0
#define CREATE_ERR -1
#define CONNECT_ERR -2
#define LINUX_MAKE_ADDRUN_ERROR -3
#define NO_LINUX_MAKE_ADDRUN_ERROR -4
#define CLOSE_ERR -5
/* 是否使用linux的本地socket命令空间 */
#define HAVE_LINUX_LOCAL_SOCKET_NAMESPACE "linux_local_socket_namespace"
#undef main_activity_LocalSocketClientActivity_MODE_PRIVATE
#define main_activity_LocalSocketClientActivity_MODE_PRIVATE 0L
#undef main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE
#define main_activity_LocalSocketClientActivity_MODE_WORLD_READABLE 1L
#undef main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE
#define main_activity_LocalSocketClientActivity_MODE_WORLD_WRITEABLE 2L
#undef main_activity_LocalSocketClientActivity_MODE_APPEND
#define main_activity_LocalSocketClientActivity_MODE_APPEND 32768L
#undef main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS
#define main_activity_LocalSocketClientActivity_MODE_MULTI_PROCESS 4L
#undef main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE
#define main_activity_LocalSocketClientActivity_BIND_AUTO_CREATE 1L
#undef main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND
#define main_activity_LocalSocketClientActivity_BIND_DEBUG_UNBIND 2L
#undef main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND
#define main_activity_LocalSocketClientActivity_BIND_NOT_FOREGROUND 4L
#undef main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT
#define main_activity_LocalSocketClientActivity_BIND_ABOVE_CLIENT 8L
#undef main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT
#define main_activity_LocalSocketClientActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
#undef main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY
#define main_activity_LocalSocketClientActivity_BIND_WAIVE_PRIORITY 32L
#undef main_activity_LocalSocketClientActivity_BIND_IMPORTANT
#define main_activity_LocalSocketClientActivity_BIND_IMPORTANT 64L
#undef main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY
#define main_activity_LocalSocketClientActivity_BIND_ADJUST_WITH_ACTIVITY 128L
#undef main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE
#define main_activity_LocalSocketClientActivity_CONTEXT_INCLUDE_CODE 1L
#undef main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY
#define main_activity_LocalSocketClientActivity_CONTEXT_IGNORE_SECURITY 2L
#undef main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED
#define main_activity_LocalSocketClientActivity_CONTEXT_RESTRICTED 4L
#undef main_activity_LocalSocketClientActivity_RESULT_CANCELED
#define main_activity_LocalSocketClientActivity_RESULT_CANCELED 0L
#undef main_activity_LocalSocketClientActivity_RESULT_OK
#define main_activity_LocalSocketClientActivity_RESULT_OK -1L
#undef main_activity_LocalSocketClientActivity_RESULT_FIRST_USER
#define main_activity_LocalSocketClientActivity_RESULT_FIRST_USER 1L
#undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE
#define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DISABLE 0L
#undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER
#define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_DIALER 1L
#undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT
#define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SHORTCUT 2L
#undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL
#define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
#undef main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL
#define main_activity_LocalSocketClientActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
/*
* Class:     main_activity_LocalSocketClientActivity
* Method:    startHeartBeat
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
客户端native方法实现:
/* 头文件begin */
#include "main_activity_LocalSocketClientActivity.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
#include <string.h>
/* 头文件end */
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     main_activity_LocalSocketClientActivity
* Method:    startHeartBeat
*/
JNIEXPORT jint JNICALL Java_main_activity_LocalSocketClientActivity_startHeartBeat(JNIEnv * env, jobject object)
{
    int socketID;
    struct sockaddr_un serverAddr;
    char path[] = "pym_local_socket\0";
    int ret;
    socketID = socket_local_client(path, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
    if (socketID < 0)
    {
        return socketID;
    }
    ret = close(socketID);
    if (ret < 0)
    {
        return CLOSE_ERR;
    }
    return NO_ERR;
}
/* 创建本地socket客户端 */
int socket_local_client(const char *name, int namespaceId, int type)
{
    int socketID;
    int ret;
    socketID = socket(AF_LOCAL, type, 0);
    if(socketID < 0)
    {
        return CREATE_ERR;
    }
    ret = socket_local_client_connect(socketID, name, namespaceId, type);
    if (ret < 0)
    {
        close(socketID);
        return ret;
    }
    return socketID;
}
/* 连接到相应的fileDescriptor*/
int socket_local_client_connect(int fd, const char *name, int namespaceId, int type)
{
    struct sockaddr_un addr;
    socklen_t socklen;
    size_t namelen;
    int ret;
    ret = socket_make_sockaddr_un(name, namespaceId, &addr, &socklen);
    if (ret < 0)
    {
        return ret;
    }
    if(connect(fd, (struct sockaddr *) &addr, socklen) < 0)
    {
        return CONNECT_ERR;
    }
    return fd;
}
/* 构造sockaddr_un */
int socket_make_sockaddr_un(const char *name, int namespaceId, struct sockaddr_un *p_addr, socklen_t *socklen)
{
    size_t namelen;
    MEM_ZERO(p_addr, sizeof(*p_addr));
#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
    namelen  = strlen(name);
    // Test with length +1 for the *initial* '\0'.
    if ((namelen + 1) > sizeof(p_addr->sun_path))
    {
        return LINUX_MAKE_ADDRUN_ERROR;
    }
    p_addr->sun_path[0] = 0;
    memcpy(p_addr->sun_path + 1, name, namelen);
#else
    namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);
    /* unix_path_max appears to be missing on linux */
    if (namelen > (sizeof(*p_addr) - offsetof(struct sockaddr_un, sun_path) - 1))
    {
        return NO_LINUX_MAKE_ADDRUN_ERROR;
    }
    strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);
    strcat(p_addr->sun_path, name);
#endif
    p_addr->sun_family = AF_LOCAL;
    *socklen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
    return NO_ERR;
}
#ifdef __cplusplus
}
#endif
注意到100~101行比较特殊,是从p_addr->sun_path[1]开始拷贝本地域名,这就是之前为什么一直connect不上的原因,至于为什么偏移1个字节来拷贝本地域名,你可以在*nix系统下输入"man 7 unix"来找到原因。
  先启动server,再启动client就可以看到结果了。对了,在成功创建并已自动连接后,我并未发送任何数据,其实发送数据就是写入文件,It's your trun now! close之前加入这段代码吧~
Int ret;
Char buf[] = “hello”;
Ret = write(socketID, buf, strlen(buf));
转载自:
http://www.cnblogs.com/zealotrouge/p/3152941.html
//////////////////////////////////////////////补充  socket_local_server 函数

#define LISTEN_BACKLOG 4
/*单次同步, 把信号量先初始化为0*/

/**
* Binds a pre-created socket(AF_LOCAL) 's' to 'name'
* returns 's' on success, -1 on fail
*
* Does not call listen()
*/
int socket_local_server_bind(int s, const char *name, int namespaceId)
{
    struct sockaddr_un addr;
    socklen_t alen;
    int n;
    int err;

    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);

    if (err < 0) {
        return -1;
    }

    /* basically: if this is a filesystem path, unlink first */
#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE
    if (1) {
#else
    if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED
        || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {
#endif
        /*ignore ENOENT*/
        unlink(addr.sun_path);
    }

    n = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));

    if(bind(s, (struct sockaddr *) &addr, alen) < 0) {
        return -1;
    }

    return s;

}

int socket_local_server(const char *name, int namespace, int type)
{
    int err;
    int s;

    s = socket(AF_LOCAL, type, 0);
    if (s < 0) return -1;

    err = socket_local_server_bind(s, name, namespace);

    if (err < 0) {
        close(s);
        return -1;
    }

    if (type == SOCK_STREAM) {
        int ret;

        ret = listen(s, LISTEN_BACKLOG);

        if (ret < 0) {
            close(s);
            return -1;
        }
    }

    return s;
}

评分

参与人数 2威望 +24 飘云币 +40 收起 理由
sndncel + 4 很给力! 为什么我看到这些英文,就头痛呢?
GGLHY + 20 + 40 赞一个!

查看全部评分

  • TA的每日心情
    开心
    2015-8-23 23:49
  • 签到天数: 27 天

    [LV.4]偶尔看看III

    发表于 2014-11-27 12:57:06 | 显示全部楼层
    学习了~~

    牛X的空道{:soso_e179:}
  • TA的每日心情
    奋斗
    2017-3-10 20:59
  • 签到天数: 243 天

    [LV.8]以坛为家I

    发表于 2014-11-27 16:20:25 | 显示全部楼层
    牛叉,顶起来
  • TA的每日心情
    奋斗
    4 天前
  • 签到天数: 235 天

    [LV.7]常住居民III

    发表于 2014-11-28 00:14:12 | 显示全部楼层
    虽然看得不少太明白,但是还是支持一下
  • TA的每日心情
    擦汗
    前天 00:19
  • 签到天数: 1319 天

    [LV.10]以坛为家III

    发表于 2014-11-28 00:14:38 | 显示全部楼层
    谢谢楼主分享呀。。。。。不过看到这么多的英文就头痛呀。。。。。
  • TA的每日心情
    开心
    5 天前
  • 签到天数: 113 天

    [LV.6]常住居民II

    发表于 2014-12-13 12:01:12 | 显示全部楼层
    这篇文章我转走。
    您需要登录后才可以回帖 登录 | 加入论坛

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    快速回复 返回顶部 返回列表