Как создать именованный канал (mkfifo) в Android?

У меня возникли проблемы с созданием именованного канала в 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. Требуется ли приложению дополнительные разрешения?

Ответ Roosmaa правильный – 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; } 

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

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

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

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

Если вам нужен IPC, лучше всего использовать Binders

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

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

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

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

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