Skip to content

Instantly share code, notes, and snippets.

@NNdroid
Forked from RajithaKumara/LocalServerSocket.md
Created August 8, 2023 06:01
Show Gist options
  • Save NNdroid/67629d7eba797fc5b6e41eda4ca7e811 to your computer and use it in GitHub Desktop.
Save NNdroid/67629d7eba797fc5b6e41eda4ca7e811 to your computer and use it in GitHub Desktop.
Android create Unix domain socket by bound file descriptor

Android create Unix domain socket by bound file descriptor

Android provide LocalServerSocket and LocalSocket to create Unix domain sockets for local interprocess communication (IPC). Unix socket address can behave in three types[1],

  • pathname
  • unnamed
  • abstract

LocalServerSocket provide two public constructors for create socket in Linux abstract namespace[2] and create socket using file descriptor that's already been created and bound[3].

Create LocalServerSocket using FileDescriptor

  • Create socket and assign address(bind) - native-lib.cpp
  • Pass bound file descriptor to LocalServerSocket - NativeClient.kt
  • Accept new connections

Create socket client

Kotlin

import android.net.LocalSocket
import android.net.LocalSocketAddress
import java.io.*
// ...
    fun createClient(pathname: String) {
        val localSocketAddress = LocalSocketAddress(pathname, LocalSocketAddress.Namespace.FILESYSTEM)

        val client = LocalSocket()
        client.connect(localSocketAddress)
        client.receiveBufferSize = 1024
        client.soTimeout = 3000
        var stream = FileInputStream(client.fileDescriptor)
        val inputReader = InputStreamReader(stream)
        val bufferReader = BufferedReader(inputReader)
        // Read buffer
        // ...
    }

C++

#include <sys/socket.h>
#include <sys/un.h>
#include <string>
#include <stdio.h>
// ...

    char *socket_path;
    struct sockaddr_un addr;
    int fd;

    socket_path = argv[0]; // Get socket path from arguments

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(-1);
    }

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
        perror("connect error");
        exit(-1);
    }

    // Read or write on socket

Node.js

const pathToSock = argv[1]; // Get socket path from arguments
const net = require('net');

const client = net.createConnection(pathToSock, () => {
    console.log('connected to server!');
});

client.on('data', (data) => {
    // ...
});

client.on('end', () => {
    console.log('disconnected from server\n');
});
#include <jni.h>
#include <string>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
extern "C" JNIEXPORT jobject JNICALL
Java_com_example_NativeClient_createBoundFileDescriptor(
JNIEnv *env,
jobject /* this */,
jstring filename) {
jfieldID field_fd;
jmethodID constructor_fd;
jclass class_fd, class_ioex;
jobject fd_object;
class_ioex = env->FindClass("java/io/IOException");
if (class_ioex == NULL) return NULL;
class_fd = env->FindClass("java/io/FileDescriptor");
if (class_fd == NULL) return NULL;
const char *socket_path = env->GetStringUTFChars(filename, NULL);
struct sockaddr_un addr;
int fd;
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
char buf[1024];
sprintf(buf, "socket: %s", strerror(errno));
env->ThrowNew(class_ioex, buf);
return NULL;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
char buf[1024];
sprintf(buf, "bind: %s", strerror(errno));
env->ThrowNew(class_ioex, buf);
return NULL;
}
env->ReleaseStringUTFChars(filename, socket_path);
constructor_fd = env->GetMethodID(class_fd, "<init>", "()V");
if (constructor_fd == NULL) return NULL;
fd_object = env->NewObject(class_fd, constructor_fd);
// Android: Renamed fd to descriptor
field_fd = env->GetFieldID(class_fd, "descriptor", "I");
if (field_fd == NULL) return NULL;
env->SetIntField(fd_object, field_fd, fd);
return fd_object;
}
package com.example
import java.io.FileDescriptor
import android.net.LocalServerSocket
import java.io.*
class NativeClient {
fun createLocalSocketServer(pathname: String) {
var projectThread = Thread(Runnable {
try {
val fd = createBoundFileDescriptor(pathname)
var server = LocalServerSocket(fd)
// While server implementation
val sender = server.accept()
//sender.sendBufferSize = 1024
//sender.receiveBufferSize = 1024
//server.close()
} catch (e: IOException) {
e.printStackTrace()
}
})
projectThread.start()
}
private external fun createBoundFileDescriptor(pathname: String): FileDescriptor?
companion object {
init {
try {
System.loadLibrary("native-lib")
} catch (error: UnsatisfiedLinkError) {
error.printStackTrace()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment