为啥从Android中的本机程序连接到LocalServerSocket时连接被拒绝

Posted

技术标签:

【中文标题】为啥从Android中的本机程序连接到LocalServerSocket时连接被拒绝【英文标题】:Why Connection refused got when connecting to LocalServerSocket from native program in Android为什么从Android中的本机程序连接到LocalServerSocket时连接被拒绝 【发布时间】:2021-04-02 14:01:05 【问题描述】:

有一个 java 服务器 LocalServerSocket 监听一个名为“myaudsocket”的抽象 unix 域套接字,如:

public void listen() 
    String name = "myaudsocket";
    mSocket = new LocalServerSocket(name);
    LocalSocket client = mSocket.accept();
    ...

我可以通过下面的 Java 代码连接服务器:

public void connect() 
    String name = "myaudsocket";
    LocalSocket client = new LocalSocket();
    client.connect(new LocalSocketAddress(name));
    Log.d("client", "connected to " + name);

但是如果我无法通过本机代码连接到服务器:

#define SOKET_NAME "@myaudsocket"
void connect() 
    char* name = SOKET_NAME;
    struct sockaddr_un addr;
    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) 
        perror("failed to create socket");
        return -1;
    

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOKET_NAME, sizeof(addr.sun_path)-1);
    if (name[0] == '@') 
       addr.sun_path[0] = '\0';
    
    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
        perror("failed to connect");
        close(sock);
        return -1;
    
    ...

控制台总是说:“连接失败:连接被拒绝”。但是上面的原生代码可以在一般的Linux OS(比如Ubuntu)上运行。

我该如何解决?

【问题讨论】:

为什么将第一个字节设置为 NUL?它是需要为 NUL 的 last 字节。 @user207421 指的是man7.org/linux/man-pages/man7/unix.7.html,据说“抽象:一个抽象套接字地址(与路径名套接字)的区别在于 sun_path[0] 是一个空字节('\ 0')。” 【参考方案1】:

问题在于地址长度。从官方参考例子https://man7.org/linux/man-pages/man7/unix.7.html来看,connect()的地址长度参数是sizeof(struct sockaddr_un)

它可以在普通的Linux系统(如Ubuntu)下工作。但它不能在android中工作。来自 libcutils 的来源:

https://android-opengrok.bangnimang.net/android-9.0.0_r61/xref/system/core/libcutils/socket_local_client_unix.cpp?r=db87e6d1#111

我们可以看到地址长度设置在结束空字符处,不包括填充零。

*alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;

改代码后为:

#define SOKET_NAME "@myaudsocket"
void connect() 
    char* name = SOKET_NAME;
    struct sockaddr_un addr;

    // add this variant
    int alen;

    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) 
        perror("failed to create socket");
        return -1;
    

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOKET_NAME, sizeof(addr.sun_path)-1);
    if (name[0] == '@') 
        addr.sun_path[0] = '\0';

    // Added this line 
    alen = offsetof(struct sockaddr_un, sun_path) + strlen(name);
    
    // change sizeof(addr) to alen
    if (connect(sock, (struct sockaddr *)&addr, alen) < 0) 
        perror("failed to connect");
        close(sock);
        return -1;
    

有效。

【讨论】:

以上是关于为啥从Android中的本机程序连接到LocalServerSocket时连接被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

我已经创建了 React 本机应用程序并将其构建为独立的 android apk 文件,但如果不连接到同一网络,我将无法使用它

在Windows中连接到远程调试器时反应本机超时

无法连接到 websocket

无法连接到 android 5.1 上的本机本地套接字

处理连接到 Web 服务的本机应用程序的登录功能

Android Wifip2p:为啥连接到组所有者后组信息为空