美文网首页
ndk开发—用socket实现双进程守护

ndk开发—用socket实现双进程守护

作者: Peakmain | 来源:发表于2019-08-08 14:43 被阅读0次

    前篇

    首先我们需要了解一下socket的通信原理,大家百度就可以找到这张图片


    socket原理.jpg
    • java实现socket通信
      首先服务器端
     ServerSocket server = new ServerSocket(8081);
            try {
                Socket client = server.accept();
                try {
                    BufferedReader input =
                            new BufferedReader(new InputStreamReader(client.getInputStream()));
                    boolean flag = true;
                    int count = 1;
     
                    while (flag) {
                        System.out.println(连接的第 + count + 次!);
                        count++;
                    }
                } finally {
                    client.close();
                }
                 
            } finally {
                server.close();
            }
        }
    

    然后客户端

     Socket client = new Socket(127.0.0.1, 8081);
    

    几个重要方法

    • 1._socket函数:对应于普通文件的打开操作
    int socket(int __af, int __type, int __protocol);
    

    1.__af:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket),AF_ROUTE等
    2.__type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
    3. __protocol:protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

    • 2.bind函数
    _socketcall int bind(int __fd, const struct sockaddr* __addr, socklen_t __addr_length);
    

    __fd:上面第一步创建得到的返回值
    __addr:指向要绑定给__fd的协议地址
    __addr_lenght:对应的是地址的长度

    • 3.listen()、connect()函数
      listen函数将socket变为被动类型的,等待客户的连接请求
      客户端通过调用connect函数来建立与TCP服务器的连接

    • 4.accept()函数
      监听客户端发送的请求,其方法会返回一个返回值代表全新的socket描述词,就是第一步返回值fd一个意思

    __socketcall int accept(int __fd, struct sockaddr* __addr, socklen_t* __addr_length);
    

    __addr:代表返回客户端的协议地址
    __addr_length参数:为协议地址的长度

    • 5.read函数
    ssize_t read(int __fd, void* __buf, size_t __count) __overloadable
        __RENAME_CLANG(read);
    

    __fd:accept返回成功后的返回值socket描述词

    • 6.execlp用一个新的进程映像替换当前进程映像

    直接贴代码

    #include <sys/select.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <pthread.h>
    #include <signal.h>
    #include <sys/wait.h>
    #include <android/log.h>
    #include <sys/types.h>
    #include <sys/un.h>
    #include <error.h>
    #include <stdlib.h>
    #include <linux/signal.h>
    #include <unistd.h>
    
    
    
    void child_do_work();
    int child_create_channel();
    void child_listen_msg();
    //com.peakmain.mall.ndk 自己的包名 peakmain.sock自己起个名字,阿猫阿狗都可以
    const char *PATH = "/data/data/com.peakmain.mall.ndk/peakmain.sock";
    int childfd;
    const char *userId;
    
    
    /**
     * 服务端读取信息
     * 客户端
     */
    int child_create_channel() {
        //int __af 协议域,又称协议族 常用的有 AF_INET 和 AF_INET6
        //int __type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)
        // int __protocol 常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议
        int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
        struct sockaddr_un addr;
        unlink(PATH);
        //清空内存
        memset(&addr, 0, sizeof(sockaddr_un));
        addr.sun_family = AF_LOCAL;
        strcpy(addr.sun_path, PATH);
        int connfd;
        if (bind(fd, (const sockaddr *) &addr, sizeof(sockaddr_un)) < 0) {
            LOGE("绑定错误");
            return 0;
        }
        LOGE("绑定成功");
        //最多同时连接5个
        listen(fd, 5);
    
    
        //while 保证宿主连接成功
        while (1) {
            //返回值 客户端的地址 //阻塞函数
            if ((connfd = accept(fd, NULL, NULL)) < 0) {
                if (errno == EINTR) {
                    continue;
                } else {
                    LOGE("读取错误");
                    return 0;
                }
            }
    
    
            childfd = connfd;
            LOGE("apk 父进程连接上 %d", childfd);
            break;
        }
        return 1;
    }
    
    /**
     * 创建服务端socket
     * 读取
     */
    void child_listen_msg() {
        fd_set rfds;
        //时间  3秒
        struct timeval timeout = {3, 0};
        while (1) {
            //清空内容
            FD_ZERO(&rfds);
            //重置
            FD_SET(childfd, &rfds);
            //选择范围 一般+1
            int range = select(childfd + 1, &rfds, NULL, NULL, &timeout);
            if (range > 0) {
                char pkg[256] = {0};
                //保证读到的信息是 指定apk客户端
                if (FD_ISSET(childfd, &rfds)) {
                    int result = read(childfd, pkg, sizeof(pkg));
                    //开启服务
                    //com.peakmain.mall.ndk.ProcessService   自己的服务全名
                    execlp("am", "am", "startservice", "--user", userId,
                           "com.peakmain.mall.ndk.ProcessService", NULL);
                    break;
                }
            }
        }
    }
    
    void child_do_work() {
        //创建和开启socket 服务端
        if (child_create_channel()) {
            child_listen_msg();
        }
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_peakmain_mall_ndk_Watcher_createWathcher(JNIEnv *env, jobject instance, jstring userId_) {
        userId = env->GetStringUTFChars(userId_, 0);
        //开双进程
        pid_t pid = fork();
        LOGE("pid是%d",pid);
        if (pid < 0) {
            //失败
        } else if (pid == 0) {
            //子进程 守护进程
            child_do_work();
        } else if (pid > 0) {
            //父进程不做处理
        }
    
        env->ReleaseStringUTFChars(userId_, userId);
    }
    //客户端
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_peakmain_mall_ndk_Watcher_connectMonitor(JNIEnv *env, jobject instance) {
        int socked;
        while (1) {
            socked = socket(AF_LOCAL, SOCK_STREAM, 0);
            if (socked < 0) {
                LOGE("客户端连接失败0");
                return;
            }
            struct sockaddr_un addr;
            //清空内存
            memset(&addr, 0, sizeof(sockaddr));
            addr.sun_family = AF_LOCAL;
            strcpy(addr.sun_path, PATH);
    
            if (connect(socked, (sockaddr *) &addr, sizeof(sockaddr_un)) < 0) {
                LOGE("客户端连接失败1");
                close(socked);
                sleep(1);
                //在来下一次尝试连接直到成功为止
                continue;
            }
            LOGE("客户端连接成功");
            break;
        }
    
    }
    

    使用service的oncreate方法

     Watcher watcher=new Watcher();
            watcher.createWathcher(String.valueOf(Process.myUid()));
            watcher.connectMonitor();
            Timer timer=new Timer();
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    Log.d("TAG","服务开启"+i);
                    i++;
                }
            },0,3*1000);
    

    大家再做的时候去结合socket图去做,然后根据图可以找到相应的方法,整体还是不难的。图片中Recv其实可以理解为Read,Send理解为write

    相关文章

      网友评论

          本文标题:ndk开发—用socket实现双进程守护

          本文链接:https://www.haomeiwen.com/subject/llkxjctx.html