Как создать именованный канал (mkfifo) в Android? - PullRequest
11 голосов
/ 29 апреля 2010

У меня проблемы с созданием именованного канала в 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_EXTERNAL_STORAGE. Я могу создать обычные файлы с точно таким же именем в одном месте, но создание канала не удается. Рассматриваемая труба должна быть доступна из нескольких приложений.

  1. Я подозреваю, что никто не может создавать каналы в / sdcard. Где это будет лучшее место для этого?
  2. Какой режим мачты мне следует установить (2-й параметр)?
  3. Нужны ли приложению какие-либо дополнительные разрешения?

Ответы [ 5 ]

14 голосов
/ 03 мая 2010

Ответ Roosmaa правильный - mkfifo () просто вызывает mknod () для создания специального файла, и FAT32 не поддерживает это.

В качестве альтернативы вы можете рассмотреть использование сокетов UNIX-доменов в «абстрактном пространстве имен» Linux. Они должны быть примерно эквивалентны именованным каналам. Вы можете получить к ним доступ по имени, но они не являются частью файловой системы, поэтому вам не придется сталкиваться с различными проблемами с разрешениями. Обратите внимание, что розетка является двунаправленной.

Поскольку это сокет, вам может потребоваться разрешение ИНТЕРНЕТ. Не уверен насчет этого.

Вот небольшой пример кода клиент / сервер:

#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;
}
8 голосов
/ 03 мая 2010

Файловая система / sdcard по умолчанию - FAT32, которая не поддерживает именованные каналы.

На устройстве без прав доступа единственное возможное место, где вы могли бы попытаться создать эти каналы, это каталог данных приложения /data/data/com.example/. Примечание: Вы не должны жестко кодировать это значение, используйте Context.getApplicationInfo (). DataDir.

Но имейте в виду, что всякий раз, когда пользователь использует Apps2SD или всякий раз, когда Google реализует эту поддержку официально, вам необходимо сообщить пользователю, что приложение не может быть сохранено в файловой системе vfat.

1 голос
/ 10 июня 2015

Я хотел бы добавить к принятому ответу:

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;
}

Показанная выше обработка ошибок является недостаточной, поскольку несколько кодов ошибок просто указывают на попытку еще раз. См. Документацию для запись .

1 голос
/ 07 мая 2010

есть также /sqlite_stmt_journals (мы используем его для тестирования, я не знаю, как долго этот каталог будет жить после обновлений ОС)

Если вам нужен IPC, рекомендуется использовать Binder

Если вам нужна только межпотоковая связь, вы можете использовать безымянные каналы через JNI (это прекрасно работает)

0 голосов
/ 08 мая 2010

Если вы кодируете это в Java, вы должны просто использовать PipedInputStream и PipedOutputStream .

...