如何在Android中创建命名管道(mkfifo)?
我在Android中创建命名管道时遇到困难,下面的示例说明了我的困境:如何在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) 我不明白这到底是为什么失败的。应用程序具
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的默认文件系统是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