我在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权限。我可以在同一位置创建具有完全相同名称的普通文件,但管道创建失败。有问题的管道应该可以从多个应用程序访问。
答案 0 :(得分:14)
Roosmaa's answer是正确的 - 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;
}
答案 1 :(得分:8)
/ sdcard的默认文件系统是FAT32,它不支持命名管道。
在非root设备上,您可以尝试创建这些管道的唯一可能位置是应用程序数据目录/data/data/com.example/。 注意:您不应该对该值进行硬编码,请使用Context.getApplicationInfo()。dataDir。
但请注意,无论何时用户使用Apps2SD或Google正式实施该支持,您都需要确保让用户知道该应用无法存储在vfat文件系统中。
答案 2 :(得分:1)
还有/sqlite_stmt_journals
(我们用它进行测试,我不知道这个目录能在OS更新中存活多久)
如果您需要IPC,最佳做法是使用Binders
如果您只需要线程间通信,可以通过JNI使用未命名的管道(这样可以正常工作)
答案 3 :(得分:1)
我想附加接受的答案:
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;
}
上面显示的错误处理不充分,因为几个错误代码只是表示再次尝试。请参阅write的文档。
答案 4 :(得分:0)
如果您使用Java编写此代码,则应使用PipedInputStream和PipedOutputStream。