Android中LocalSocket实战

来源:互联网 发布:淘宝叶罗丽娃娃 编辑:程序博客网 时间:2024/06/04 19:20

网上LocalSocket的知识可以搜到你读不完,所以它的知识本文就不介绍了。本文着重的是自己在实际使用中遇到的问题。

虽然早就经过各种代码洗礼,但是对于LocalSocket这方面还是第一次用到。但是咱不怕,发挥程序猿顶着油锅也要上的精神。巴拉巴拉把LocalSocket的基本知识过了一边。

看了看在Android中怎么用的,以及代码怎么写的。

还是先说一下背景吧,否则会凌乱哈。

本人需要在native层的sdcard应用中增加一个通信机制,可以接收到framework层传来的消息。经过一番讨论,决定使用LocalSocket。Android中有好多例子,咱不怕。

于是就开搞,我去看别的应用使用的地方是怎么办的。

以vold为例

(1)增加系统socket资源

service vold /system/bin/vold \        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0    class core    socket vold stream 0660 root mount    socket cryptd stream 0660 root mount

上述代码在vold.rc文件中。在vold启动的时候呢,创建了两个socket资源vold和cryptd。它们可以在vold进程中使用,因为它们关联到一起了。

(2)native层服务端

通常在服务端会有如下类似代码,SOCKET_PATH就是一个字符串,可以是vold啊,或者cryptd

int main(const int argc, const char *argv[])
{
    //获取已绑定socket
    lsocket = android_get_control_socket(SOCKET_PATH);
    //监听socket
    listen(lsocket, 5);
    for (;;) {
        //等待客户端建立连接
        s = accept(lsocket, &addr, &alen);
        for (;;) {
                 //接收数据 相当于recv
                 readx(s, buf, count);
                 //执行相关的操作
                execute(s, buf);
        }
        //关闭socket
        close(s);
    }
}

(3)framework层客户端

通常客户端有如下代码

boolean connect()
{
       //创建socket
       mSocket = new LocalSocket();
       //设置连接地址
       LocalSocketAddress address = new             LocalSocketAddress("vold",
                    LocalSocketAddress.Namespace.RESERVED);
       //建立连接
       mSocket.connect(address);
       //获取数据输入流 可以读数据
       mIn = mSocket.getInputStream();
       //获取数据输出流 可以写数据
       mOut = mSocket.getOutputStream();
}
其实上面这个123我是可以不写的,因为也不是文章要写的重点嘛,默认大家都会的嘛。

其实看完上面,如果大家有实际需要的话,问题也就来了:这个应用要不是从*.rc文件中启动的怎么办呢,如何创建socket资源文件与之绑定,从而可以在代码中使用呢。

巧了,还真有,我遇到的这个问题就是。sdcard是通过vold应用在代码中启动的。

好,下面就来港货了(不一定比陆获好)。

int lsocket = socket_local_server("sdcard", ANDROID_SOCKET_NAMESPACE_ABSTRACT,                              SOCK_STREAM | SOCK_CLOEXEC);

// Linux "abstract" (non-filesystem) namespace
#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0
// Android "reserved" (/dev/socket) namespace
#define ANDROID_SOCKET_NAMESPACE_RESERVED 1
// Normal filesystem namespace
#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2

这里一定要用ANDROID_SOCKET_NAMESPACE_ABSTRACT,是重点啊,要划,。其实socket_local_server这个函数也很牛逼,它创建了socket之后还绑定了,连接了。

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_TYPE_MASK) == SOCK_STREAM) {
        int ret;
        ret = listen(s, LISTEN_BACKLOG);
        if (ret < 0) {
            close(s);
            return -1;
        }
    }
    return s;
}

其实这样就创建成功了。

然后客户端这么使用就行了。

private static boolean openCryptSocket() {    try {        mSocket = new LocalSocket(LocalSocket.SOCKET_STREAM);        LocalSocketAddress address = new LocalSocketAddress("sdcard",                LocalSocketAddress.Namespace.ABSTRACT);        mSocket.connect(address);        mOutputStream = mSocket.getOutputStream();    } catch (IOException ex) {        Slog.w(TAG, "encrypt daemon socket open failed");        mSocket = null;        return false;    }    return true;}

上面把问题解决了。但是一般实际应用中你还会遇到权限问题。所以要遭sdcardd.te中添加allow sdcardd vold:unix_stream_socket {connectto read write};这个是是要sdcard这个应用有对socket流的读写连接权限。我是在framework的也给服务中添加上述客户端代码的。所以还要在system_server.te文件中添加allow system_server sdcardd:unix_stream_socket {connectto read write};这句话。

好了权限问题也ok了。

原创粉丝点击