如何在Android中创建命名管道(mkfifo)?

如何在Android中创建命名管道(mkfifo)?,android,android-ndk,named-pipes,Android,Android Ndk,Named Pipes,我在Android中创建命名管道时遇到困难,下面的示例说明了我的困境: res = mkfifo("/sdcard/fifo9000", S_IRWXO); if (res != 0) { LOG("Error while creating a pipe (return:%d, errno:%d)", res, errno); } 代码始终打印: Error while creating a pipe (return:-1, errno:1) 我不明白这到底是为什么失败的。应用程序具

我在Android中创建命名管道时遇到困难,下面的示例说明了我的困境:

res = mkfifo("/sdcard/fifo9000", S_IRWXO);
if (res != 0)
{
    LOG("Error while creating a pipe (return:%d, errno:%d)", res, errno);
}
代码始终打印:

Error while creating a pipe (return:-1, errno:1)
我不明白这到底是为什么失败的。应用程序具有android.permission.WRITE\u外部存储权限。我可以在同一位置创建名称完全相同的普通文件,但管道创建失败。有问题的管道应该可以从多个应用程序访问

  • 我怀疑没有人可以在/sdcard中创建管道。在哪里最好
  • 我应该设置什么模式桅杆(第二个参数)
  • 应用程序是否需要任何额外的权限

  • /sdcard的默认文件系统是FAT32,它不支持命名管道

    在非根设备上,您可以尝试创建这些管道的唯一可能位置是应用程序数据目录/data/data/com.example/。 注意:不应硬编码该值,请使用Context.getApplicationInfo().dataDir

    但请注意,无论用户何时使用Apps2SD或谷歌何时正式实施该支持,您都需要确保让用户知道该应用程序不能存储在vfat文件系统上。

    是正确的——mkfifo()只调用mknod()创建一个特殊文件,而FAT32不支持这一点

    作为一种选择,您可能需要考虑使用Linux的“抽象名称空间”UNIX域套接字。它们应该大致相当于命名管道。您可以按名称访问它们,但它们不是文件系统的一部分,因此您不必处理各种权限问题。注意:插座是双向的

    由于它是一个套接字,您可能需要INTERNET许可。我不确定

    下面是一个简单的客户机/服务器示例代码:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stddef.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    
    /*
     * Create a UNIX-domain socket address in the Linux "abstract namespace".
     *
     * The socket code doesn't require null termination on the filename, but
     * we do it anyway so string functions work.
     */
    int makeAddr(const char* name, struct sockaddr_un* pAddr, socklen_t* pSockLen)
    {
        int nameLen = strlen(name);
        if (nameLen >= (int) sizeof(pAddr->sun_path) -1)  /* too long? */
            return -1;
        pAddr->sun_path[0] = '\0';  /* abstract namespace */
        strcpy(pAddr->sun_path+1, name);
        pAddr->sun_family = AF_LOCAL;
        *pSockLen = 1 + nameLen + offsetof(struct sockaddr_un, sun_path);
        return 0;
    }
    
    int main(int argc, char** argv)
    {
        static const char* message = "hello, world!";
        struct sockaddr_un sockAddr;
        socklen_t sockLen;
        int result = 1;
    
        if (argc != 2 || (argv[1][0] != 'c' && argv[1][0] != 's')) {
            printf("Usage: {c|s}\n");
            return 2;
        }
    
        if (makeAddr("com.whoever.xfer", &sockAddr, &sockLen) < 0)
            return 1;
        int fd = socket(AF_LOCAL, SOCK_STREAM, PF_UNIX);
        if (fd < 0) {
            perror("client socket()");
            return 1;
        }
    
        if (argv[1][0] == 'c') {
            printf("CLIENT %s\n", sockAddr.sun_path+1);
    
            if (connect(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) {
                perror("client connect()");
                goto bail;
            }
            if (write(fd, message, strlen(message)+1) < 0) {
                perror("client write()");
                goto bail;
            }
        } else if (argv[1][0] == 's') {
            printf("SERVER %s\n", sockAddr.sun_path+1);
            if (bind(fd, (const struct sockaddr*) &sockAddr, sockLen) < 0) {
                perror("server bind()");
                goto bail;
            }
            if (listen(fd, 5) < 0) {
                perror("server listen()");
                goto bail;
            }
            int clientSock = accept(fd, NULL, NULL);
            if (clientSock < 0) {
                perror("server accept");
                goto bail;
            }
            char buf[64];
            int count = read(clientSock, buf, sizeof(buf));
            close(clientSock);
            if (count < 0) {
                perror("server read");
                goto bail;
            }
            printf("GOT: '%s'\n", buf);
        }
        result = 0;
    
    bail:
        close(fd);
        return result;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    /*
    *在Linux“抽象命名空间”中创建UNIX域套接字地址。
    *
    *套接字代码不要求文件名以null结尾,但
    *我们无论如何都要这样做,所以字符串函数可以工作。
    */
    int makeAddr(const char*name,struct sockaddr\u un*pAddr,socklen\u t*pSockLen)
    {
    int nameLen=strlen(名称);
    如果(nameLen>=(int)sizeof(pAddr->sun_path)-1)/*太长*/
    返回-1;
    pAddr->sun_路径[0]='\0';/*抽象名称空间*/
    strcpy(pAddr->sun_路径+1,名称);
    pAddr->sun_family=AF_LOCAL;
    *pSockLen=1+nameLen+offsetof(结构sockaddr\u un,太阳路径);
    返回0;
    }
    int main(int argc,字符**argv)
    {
    static const char*message=“你好,世界!”;
    结构sockaddr_un sockaddr;
    socklen_t socklen;
    int结果=1;
    if(argc!=2 | |(argv[1][0]!='c'&argv[1][0]!='s')){
    printf(“用法:{c|s}\n”);
    返回2;
    }
    if(makeAddr(“com.where.xfer”、&sockAddr、&sockLen)<0)
    返回1;
    int fd=套接字(AF_本地、SOCK_流、PF_UNIX);
    如果(fd<0){
    perror(“客户端套接字()”);
    返回1;
    }
    如果(argv[1][0]=='c'){
    printf(“客户端%s\n”,sockAddr.sun\u路径+1);
    if(连接(fd,(常量结构sockaddr*)和sockaddr,sockLen)<0){
    perror(“客户端连接()”);
    去保释;
    }
    如果(写入(fd、消息、strlen(消息)+1)<0){
    perror(“客户端写入()”);
    去保释;
    }
    }else if(argv[1][0]='s'){
    printf(“服务器%s\n”,sockAddr.sun\u路径+1);
    if(绑定(fd,(常量结构sockaddr*)和sockaddr,sockLen)<0){
    perror(“服务器绑定()”);
    去保释;
    }
    如果(听(fd,5)<0){
    perror(“server listen()”);
    去保释;
    }
    int clientSock=accept(fd,NULL,NULL);
    if(clientSock<0){
    perror(“服务器接受”);
    去保释;
    }
    char-buf[64];
    int count=读取(clientSock、buf、sizeof(buf));
    关闭(clientSock);
    如果(计数<0){
    perror(“服务器读取”);
    去保释;
    }
    printf(“得到:'%s'\n',buf);
    }
    结果=0;
    保释:
    关闭(fd);
    返回结果;
    }
    
    还有
    /sqlite\u stmt\u日志
    (我们使用它进行测试,我不知道这个目录在操作系统更新后还能保存多久)

    如果需要IPC,最佳做法是使用


    如果您只需要线程间通信,那么可以通过JNI使用未命名管道(这很好)

    如果您是用Java编写的,那么只需使用and即可。

    我想在接受的答案后面附加以下内容:

    1) 我能够使用这种方法在Android应用程序的两个本机模块之间连接套接字

    2)
    write()
    应该处于循环中,因为它可能不会在第一次执行时写入请求的全部金额。例如,它应该是这样的:

    void *p = buffer;
    count = 0;
    while ((count += write(clientSock, buffer, num_bytes - count)) < num_bytes)
    {
        if (count < 0)
        {
            close(clientSock);
            errCode = count;
            break;
        }
        p += count;
    }
    
    void*p=缓冲区;
    计数=0;
    而((count+=write(clientSock,buffer,num_bytes-count))

    上面显示的错误处理是不够的,因为几个错误代码只是指示重试。请参阅文档。

    这可能很难在java中复制-只需说:)True,但原始代码片段也不是java,因此我认为这不是交易的破坏者。套接字方法肯定比文件系统中的命名管道更需要JNI。对,目标是C。这种方法似乎要求每个连接的客户机有单独的线程。Berkus:套接字调用需要字符串长度,而不是以null结尾的字符串,因此您需要计算并传入它。(对于空终止和过长,它可能工作得很好,但我相信以上是学究式的正确方法。)自android 5.1以来,套接字存在一些问题。看我的问题:是的,这就是我最终使用的。这里的关键是让本机代码与本机代码对话。复习Java会使它变得更复杂(JNI和l