NDK20_socket实现双进程守护

NDK开发汇总

一 需求

APP保活:设置监听,当应用服务被停止后,重新唤醒应用服务

二原理

利用socket机制,监听 app主进程被干掉

1 搭建服务器端

a)、创建ServerSocket对象绑定监听端口。
b)、通过accept()方法监听客户端的请求。
c)、建立连接后,通过输入输出流读取客户端发送的请求信息。
d)、通过输出流向客户端发送请求信息。
e)、关闭相关资源。

类似的Java服务端

ServerSocket server=new ServerSocket(5209);

  socket=server.accept();
	while(1){
	//读取空内容
	BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
	line=br.readLine();
	
	}

    socket.close(); //关闭Socket
    server.close(); //关闭ServerSocket

2 客户端

类似java的获取输入流,并读取服务器端的响应信息

 Socket socket = new Socket("192.168.1.115", 5209);

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
socket.close(); // 关闭Socket

3 重要的JNI层Sock函数

int socket()函数

    int  socket(int protofamily, int type, int protocol);//返回sockfd
  • socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字, 而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

  • protofamily:即协议域,又称为协议族(family)。常用的协议族有:
    AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,

  • type:指定socket类型。

  • 常用的socket类型SOCK_STREAM IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,
    它们分别对应 流协议,TCP传输协议、UDP传输协议
    、 STCP传输协议、TIPC传输协议

bind()函数

  
   int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
  • addr:一个const struct sockaddr 指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同
    ● Linux域对应的是:
    struct sockaddr_un {
    sa_family_t sun_family; /
    AF_UNIX /
    char sun_path[UNIX_PATH_MAX]; /
    pathname */
    };
  • addrlen:对应的是地址的长度。

三 代码实现

#include 
#include 
#include "native-lib.h"

int m_child;
int m_parent = -1;

const  char * user_id;
const char *PATH = "/data/data/com.dongnao.socketprocess/my.sock";
extern "C"{
JNIEXPORT void JNICALL
Java_com_dongnao_socketprocess_Wathcer_createWatcher(JNIEnv *env, jobject instance, jstring userId) {
    user_id = (const char *) env->GetStringChars(userId, 0);
    create_child();
}

JNIEXPORT void JNICALL
Java_com_dongnao_socketprocess_Wathcer_connectToMonitor(JNIEnv *env, jobject instance) {
//    子进程1    父进程2
    int sockfd;
    struct sockaddr_un  addr;
    while (1) {
        LOGE("客户端  父进程开始连接");
        sockfd=socket(AF_LOCAL, SOCK_STREAM, 0);
        if (sockfd < 0) {
            return;
        }
        memset(&addr, 0, sizeof(sockaddr_un));
        addr.sun_family = AF_LOCAL;
        strcpy(addr.sun_path, PATH);
        if (connect(sockfd, (const sockaddr *) &addr, sizeof(addr)) < 0) {
            LOGE("连接失败  休眠");
//            连接失败
            close(sockfd);
            sleep(1);
//            再来继续下一次尝试
            continue;
        }
//        连接成功
        m_parent = sockfd;
        LOGE("连接成功  父进程跳出循环");
        break;
    }

}

}

void create_child() {
//
    pid_t pid = fork();
//
    if (pid < 0) {

    } else if (pid > 0) {
//父进程
    } else if (pid == 0){
        LOGE("子进程开启 ");
//       守护进程
        child_do_work();
    }


}

void child_do_work() {
//    守护进程
//   1 建立socket服务
//    2读取消息
    if(child_create_channel()) {
        child_listen_msg();
    }

}

void child_listen_msg() {
    fd_set rfds;
    while (1) {
        //清空端口号
        FD_ZERO(&rfds);
        FD_SET(m_child,&rfds);
//        设置超时时间
        struct timeval timeout={3,0};
        int r=select(m_child + 1, &rfds, NULL, NULL, &timeout);
        LOGE("读取消息前  %d  ",r);
        if (r > 0) {
            char pkg[256] = {0};
//            确保读到的内容是制定的端口号
            if (FD_ISSET(m_child, &rfds)) {
//                阻塞式函数  客户端写到内容  apk进程  没有进行任何写入    连接
                int result = read(m_child, pkg, sizeof(pkg));
//                读到内容的唯一方式 是客户端断开
                LOGE("重启父进程  %d ",result);
                LOGE("读到信息  %d    userid  %d ",result,user_id);

                execlp("am", "am", "startservice", "--user",user_id,
                       "com.dongnao.socketprocess/com.dongnao.socketprocess.ProcessService", (char*)NULL);
                break;
            }
        }



    }
}

int child_create_channel() {
//    创建socket  listenfd 对象
    int listenfd=socket(AF_LOCAL, SOCK_STREAM, 0);
//    取消之前进程文件连接
    unlink(PATH);
    struct sockaddr_un  addr;
//    清空内存
    memset(&addr, 0, sizeof(sockaddr_un));
    addr.sun_family = AF_LOCAL;

    strcpy(addr.sun_path, PATH);
    int connfd=0;
    LOGE("绑定端口号");
    if(bind(listenfd, (const sockaddr *) &addr, sizeof(addr))<0) {
        LOGE("绑定错误");
        return 0;
    }
    listen(listenfd, 5);
    while (1) {
        LOGE("子进程循环等待连接  %d ",m_child);
//        不断接受客户端请求的数据
//        等待 客户端连接  accept阻塞式函数
        if ((connfd = accept(listenfd, NULL, NULL)) < 0) {
            if (errno == EINTR) {
                continue;
            } else{
                LOGE("读取错误");
                return 0;
            }
        }
//        apk 进程连接上了
        m_child = connfd;
        LOGE("apk 父进程连接上了  %d ",m_child);
        break;
    }
    LOGE("返回成功");
    return 1;
}

四 Demo

Socket进程守护.zip

你可能感兴趣的:(NDK)