Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android LocalServerSocket

In android, there are two classes LocalServerSocket and LocalSocket. I think they are something like AF_LOCAL in unix socket (I am not sure it is correct or not).

My question is that : Is it possible to create LocalServerSocket in Java and use a normal unix socket client to connect to it in native or other process ?

If it is possible, what the "sockaddr_un.sun_path" I should set in native ?

I have written a sample project to test it, and I try to set the .sun_path as same as string name used in LocalServerSocket, but it failed, the native could not connect to the Java LocalServerSocket.

My Java code :

package test.socket;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class TestSocketActivity extends Activity {

    public static String SOCKET_ADDRESS = "my.local.socket.address";
    public String TAG = "Socket_Test";


    static{System.loadLibrary("testSocket");}
    private native void clientSocketThreadNative();
    private native void setStopThreadNative();
    localServerSocket mLocalServerSocket;
    localClientSocket mLocalClientSocket;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mLocalServerSocket = new localServerSocket();

        mLocalClientSocket = new localClientSocket();
    }

    /* LocalServerSocket */
    public class localServerSocket extends Thread {

        int bufferSize = 32;
        byte[] buffer;
        int bytesRead;
        int totalBytesRead;
        int posOffset;
        LocalServerSocket server;
        LocalSocket receiver;
        InputStream input;
        private volatile boolean stopThread;

        public localServerSocket() {
            Log.d(TAG, " +++ Begin of localServerSocket() +++ ");
            buffer = new byte[bufferSize];
            bytesRead = 0;
            totalBytesRead = 0;
            posOffset = 0;

            try {
                server = new LocalServerSocket(SOCKET_ADDRESS);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.d(TAG, "The LocalServerSocket created failed !!!");
                e.printStackTrace();
            }

            stopThread = false;
        }

        public void run() {             
            Log.d(TAG, " +++ Begin of run() +++ ");
                while (!stopThread) {

                    if (null == server){
                        Log.d(TAG, "The LocalServerSocket is NULL !!!");
                        stopThread = true;
                        break;
                    }

                    try {
                        Log.d(TAG, "LocalServerSocket begins to accept()");
                        receiver = server.accept();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        Log.d(TAG, "LocalServerSocket accept() failed !!!");
                        e.printStackTrace();
                        continue;
                    }                   

                    try {
                        input = receiver.getInputStream();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        Log.d(TAG, "getInputStream() failed !!!");
                        e.printStackTrace();
                        continue;
                    }

                    Log.d(TAG, "The client connect to LocalServerSocket");

                    while (receiver != null) {

                        try {
                            bytesRead = input.read(buffer, posOffset,
                                    (bufferSize - totalBytesRead));
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            Log.d(TAG, "There is an exception when reading socket");
                            e.printStackTrace();
                            break;
                        }

                        if (bytesRead >= 0) {
                            Log.d(TAG, "Receive data from socket, bytesRead = "
                                    + bytesRead);
                            posOffset += bytesRead;
                            totalBytesRead += bytesRead;
                        }

                        if (totalBytesRead == bufferSize) {
                            Log.d(TAG, "The buffer is full !!!");
                            String str = new String(buffer);
                            Log.d(TAG, "The context of buffer is : " + str);

                            bytesRead = 0;
                            totalBytesRead = 0;
                            posOffset = 0;
                        }

                    }
                    Log.d(TAG, "The client socket is NULL !!!");
                }
                Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
                if (receiver != null){
                    try {
                        receiver.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if (server != null){
                    try {
                        server.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
        }

        public void setStopThread(boolean value){
            stopThread = value;
            Thread.currentThread().interrupt(); // TODO : Check
        }

    }




    /* Client native socket */
    public class localClientSocket extends Thread {

        private volatile boolean stopThread;

        public localClientSocket(){
            Log.d(TAG, " +++ Begin of localClientSocket() +++ ");
            stopThread = false;
        }

        public void run(){
            Log.d(TAG, " +++ Begin of run() +++ ");
            while(!stopThread){
                clientSocketThreadNative();
            }
        }

        public void setStopThread(boolean value){
            stopThread = value;
            setStopThreadNative();
            Thread.currentThread().interrupt(); // TODO : Check
        }
    }


    public void bt_startServerOnClick(View v) {
        mLocalServerSocket.start();
    }

    public void bt_startClientOnClick(View v) {
        mLocalClientSocket.start();
    }

    public void bt_stopOnClick(View v) {
        mLocalClientSocket.setStopThread(true);
        mLocalServerSocket.setStopThread(true);
    }

}

My Native code :

#define SOCKET_NAME "my.local.socket.address"

JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
  (JNIEnv *env, jobject object){

    LOGD("In clientSocketThreadNative() : Begin");

    stopThread = 1;

    int sk, result;
    int count = 1;
    int err;

    char *buffer = malloc(8);

    int i;
    for(i = 0; i<8; i++){
        buffer[i] = (i+1);
    }

    /*
    struct sockaddr_un addr;

    bzero((char *)&addr,sizeof(addr);
    addr.sun_family = AF_UNIX;
    addr.sun_path = SOCKET_NAME;
    */

    struct sockaddr_un addr = {
        AF_UNIX, SOCKET_NAME
    };

    LOGD("In clientSocketThreadNative() : Before creating socket");
    sk = socket(PF_LOCAL, SOCK_STREAM, 0);

    if (sk < 0) {
        err = errno;
        LOGD("%s: Cannot open socket: %s (%d)\n",
            __FUNCTION__, strerror(err), err);
        errno = err;
        return;
    }

    LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
    if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        err = errno;
        LOGD("%s: connect() failed: %s (%d)\n",
            __FUNCTION__, strerror(err), err);
        close(sk);
        errno = err;
        return;
    }

    LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");

    while(!stopThread){
        result = write(sk, buffer, 8);
        LOGD("In clientSocketThreadNative() : Total write = %d", result);
        count++;
        if(4 == count){
            sleep(1);
            count = 0;
        } 
    }

    LOGD("In clientSocketThreadNative() : End");
}

Any suggestion would be greatly appreciated !!!

like image 997
Bohan Lu Avatar asked Sep 22 '11 14:09

Bohan Lu


2 Answers

The following code might not be perfect but it works !!! Thanks for Mike.

Java part (Socket Server) :

package test.socket;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class TestSocketActivity extends Activity {

    public static String SOCKET_ADDRESS = "/test/socket/localServer";
    public String TAG = "Socket_Test";


    static{System.loadLibrary("testSocket");}
    private native void clientSocketThreadNative();
    private native void setStopThreadNative();
    localSocketServer mLocalSocketServer;
    localSocketClient mLocalSocketClient;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mLocalSocketServer = new localSocketServer();

        mLocalSocketClient = new localSocketClient();
    }

    /* LocalSocketServer */
    public class localSocketServer extends Thread {

        int bufferSize = 32;
        byte[] buffer;
        int bytesRead;
        int totalBytesRead;
        int posOffset;
        LocalServerSocket server;
        LocalSocket receiver;
        InputStream input;
        private volatile boolean stopThread;

        public localSocketServer() {
            Log.d(TAG, " +++ Begin of localSocketServer() +++ ");
            buffer = new byte[bufferSize];
            bytesRead = 0;
            totalBytesRead = 0;
            posOffset = 0;

            try {
                server = new LocalServerSocket(SOCKET_ADDRESS);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                Log.d(TAG, "The localSocketServer created failed !!!");
                e.printStackTrace();
            }

             LocalSocketAddress localSocketAddress; 
             localSocketAddress = server.getLocalSocketAddress();
             String str = localSocketAddress.getName();

             Log.d(TAG, "The LocalSocketAddress = " + str);

            stopThread = false;
        }

        public void run() {             
            Log.d(TAG, " +++ Begin of run() +++ ");
                while (!stopThread) {

                    if (null == server){
                        Log.d(TAG, "The localSocketServer is NULL !!!");
                        stopThread = true;
                        break;
                    }

                    try {
                        Log.d(TAG, "localSocketServer begins to accept()");
                        receiver = server.accept();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        Log.d(TAG, "localSocketServer accept() failed !!!");
                        e.printStackTrace();
                        continue;
                    }                   

                    try {
                        input = receiver.getInputStream();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        Log.d(TAG, "getInputStream() failed !!!");
                        e.printStackTrace();
                        continue;
                    }

                    Log.d(TAG, "The client connect to LocalServerSocket");

                    while (receiver != null) {

                        try {
                            bytesRead = input.read(buffer, posOffset,
                                    (bufferSize - totalBytesRead));
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            Log.d(TAG, "There is an exception when reading socket");
                            e.printStackTrace();
                            break;
                        }

                        if (bytesRead >= 0) {
                            Log.d(TAG, "Receive data from socket, bytesRead = "
                                    + bytesRead);
                            posOffset += bytesRead;
                            totalBytesRead += bytesRead;
                        }

                        if (totalBytesRead == bufferSize) {
                            Log.d(TAG, "The buffer is full !!!");
                            String str = new String(buffer);
                            Log.d(TAG, "The context of buffer is : " + str);

                            bytesRead = 0;
                            totalBytesRead = 0;
                            posOffset = 0;
                        }

                    }
                    Log.d(TAG, "The client socket is NULL !!!");
                }
                Log.d(TAG, "The LocalSocketServer thread is going to stop !!!");
                if (receiver != null){
                    try {
                        receiver.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if (server != null){
                    try {
                        server.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
        }

        public void setStopThread(boolean value){
            stopThread = value;
            Thread.currentThread().interrupt(); // TODO : Check
        }

    }

    /* Client native socket */
    public class localSocketClient extends Thread {

        private volatile boolean stopThread;

        public localSocketClient(){
            Log.d(TAG, " +++ Begin of localSocketClient() +++ ");
            stopThread = false;
        }

        public void run(){
            Log.d(TAG, " +++ Begin of run() +++ ");
            while(!stopThread){
                clientSocketThreadNative();
            }
        }

        public void setStopThread(boolean value){
            stopThread = value;
            setStopThreadNative();
            Thread.currentThread().interrupt(); // TODO : Check
        }
    }


    public void bt_startServerOnClick(View v) {
        mLocalSocketServer.start();
    }

    public void bt_startClientOnClick(View v) {
        mLocalSocketClient.start();
    }

    public void bt_stopOnClick(View v) {
        mLocalSocketClient.setStopThread(true);
        mLocalSocketServer.setStopThread(true);
    }

}

Native C part (Client)

#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/un.h>


#include "test_socket_TestSocketActivity.h"

#define LOCAL_SOCKET_SERVER_NAME "/test/socket/localServer"

volatile int stopThread;

#ifndef __JNILOGGER_H_
#define __JNILOGGER_H_ 
#include <android/log.h> 
#ifdef __cplusplus
extern "C" {
#endif
#ifndef LOG_TAG
#define LOG_TAG "NativeSocket"
#endif
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,LOG_TAG,__VA_ARGS__)
#define LOGS(...) __android_log_print(ANDROID_LOG_SILENT,LOG_TAG,__VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif /* __JNILOGGER_H_ */ 


JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_clientSocketThreadNative
  (JNIEnv *env, jobject object){

    LOGD("In clientSocketThreadNative() : Begin");

    stopThread = 0;

    int sk, result;
    int count = 1;
    int err;

    char *buffer = malloc(8);

    int i;
    for(i = 0; i<8; i++){
        buffer[i] = (i+1);
    }

    struct sockaddr_un addr;
    socklen_t len;
    addr.sun_family = AF_LOCAL;
    /* use abstract namespace for socket path */
    addr.sun_path[0] = '\0';
    strcpy(&addr.sun_path[1], LOCAL_SOCKET_SERVER_NAME );
    len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&addr.sun_path[1]);

    LOGD("In clientSocketThreadNative() : Before creating socket");
    sk = socket(PF_LOCAL, SOCK_STREAM, 0);
    if (sk < 0) {
        err = errno;
        LOGD("%s: Cannot open socket: %s (%d)\n",
            __FUNCTION__, strerror(err), err);
        errno = err;
        return;
    }

    LOGD("In clientSocketThreadNative() : Before connecting to Java LocalSocketServer");
    if (connect(sk, (struct sockaddr *) &addr, len) < 0) {
        err = errno;
        LOGD("%s: connect() failed: %s (%d)\n",
            __FUNCTION__, strerror(err), err);
        close(sk);
        errno = err;
        return;
    }

    LOGD("In clientSocketThreadNative() : Connecting to Java LocalSocketServer succeed");
    while(!stopThread){
        result = write(sk, buffer, 8);
        LOGD("In clientSocketThreadNative() : Total write = %d", result);
        count++;
        if(4 == count){
            sleep(1);
            count = 0;
        } 
    }


    LOGD("In clientSocketThreadNative() : End");
}


JNIEXPORT void JNICALL Java_test_socket_TestSocketActivity_setStopThreadNative
  (JNIEnv *env, jobject object){

    stopThread = 1;

}
like image 187
Bohan Lu Avatar answered Sep 22 '22 23:09

Bohan Lu


Looking at local_socket_client.c in the Android source, it looks like they do this:

int socket_make_sockaddr_un(const char *name, int namespaceId, 
        struct sockaddr_un *p_addr, socklen_t *alen)
{
    memset (p_addr, 0, sizeof (*p_addr));
    size_t namelen;

    switch (namespaceId) {
        case ANDROID_SOCKET_NAMESPACE_ABSTRACT:
            namelen  = strlen(name);

            // Test with length +1 for the *initial* '\0'.
            if ((namelen + 1) > sizeof(p_addr->sun_path)) {
                goto error;
            }

            /*
             * Note: The path in this case is *not* supposed to be
             * '\0'-terminated. ("man 7 unix" for the gory details.)
             */

            p_addr->sun_path[0] = 0;
            memcpy(p_addr->sun_path + 1, name, namelen);

            ...
    p_addr->sun_family = AF_LOCAL;
    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;

It seems like the memset() is important because the entire sun_path is relevant. (looks like you cover that part with your structure initialization, though.) And it's not "0" plus the original name, it's an actual zero byte! (its value is all binary zeroes, not an ascii '0')

Try following more closely what they're doing, including the leading '\0' byte and the AF_LOCAL family.

If you have updated code (whether it works or not) please post it! I'm interested in your results. Did you ever get this to work?

If it doesn't work, find out what errno is and either call perror() to print it to stderr, or call strerror() and log the output. Let us know what error you get.


Edit

I recently solved this problem in a project of my own. I found that the key was to specify the correct length when calling connect() and bind(). In the code I posted above, it calculates the length by using the offset of the sun_path in the structure, plus the length of the name, plus one for the leading '\0' byte. If you specify any other length, Java code might not be able to connect to the socket.

like image 31
mpontillo Avatar answered Sep 22 '22 23:09

mpontillo