开发者

JNI Poll problem

I write a native method that could receive data from socket and then write back to a ByteArray which is input parameter from Java. The socket was created in BlueZ and transmitted to my program by dBus message. I use a separated thread to do the whole procedure.

Thanks for Cerber's suggestion for my previous GetPrimitiveArrayCritical() problem. Now, the program could run without error.

However, the new problem is that because I use Poll to wait for POLLIN event, if there is incoming data available for reading, there would be POLLIN event theoretically and I could do the socket read.

Unfortunatedly the POLLIN event is continuously triggered but I could not read any data !!! But this strange behavior didn't happen when I did the whole same procedure in BlueZ's code. I am sure the socket is correct.

The piece of my native code are something like :

struct socket_loop_native_data {
        pthread_mutex_t thread_mutex;
        pthread_t thread;
        struct pollfd *pollData;
        JavaVM *vm;
        int envVer;
        jobject me;
        jbyteArray javaBuffer;
        int bufferSize;
        jbyte *nativeBuffer;
        char *beginOfBuffer;
        char *endOfBuffer;
        int decodedDataSize;
        bool running;
};

typedef socket_loop_native_data native_data_t;

static jfieldID field_mNativeDataSocket;

static inline native_data_t *get_native_data(JNIEnv *env, jobject object) {
    return (native_data_t *)(env->GetIntField(object, field_mNativeDataSocket));
}

native_data_t *get_SocketLoop_native_data(JNIEnv *env, jobject object) {
    return get_native_data(env, object);
}

JNIEXPORT void JNICALL Java_android_classInitNativeSocket(JNIEnv* env, jclass clazz) {
    field_mNativeDataSocket = env->GetFieldID(clazz, "mNativeDataSocket", "I");
}

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

    native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
    if (NULL == nat) {
        LOGD("%s: out of memory!", __FUNCTION__);
        return;
    }
    memset(nat, 0, sizeof(native_data_t));

    pthread_mutex_init(&(nat->thread_mutex), NULL);

    env->SetIntField(object, field_mNativeDataSocket, (jint)nat);

}

JNIEXPORT jboolean JNICALL Java_android_startSocketLoopNative(JNIEnv *env, jobject object, jint sock, jbyteArray buffer, jint size) {

    jboolean result = JNI_FALSE;

    socket_loop_native_data *nat = get_native_data(env, object);

    pthread_mutex_lock(&(nat->thread_mutex));

    nat->running = false;

    if (nat->pollData) {
        LOGD("trying to start SocketLoop a second time!");
        pthread_mutex_unlock( &(nat->thread_mutex) );
        return JNI_FALSE;
    }

    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd));
    if (!nat->pollData) {
        LOGD("out of memory error starting SocketLoop!");
        goto done;
    }

    memset(nat->pollData, 0, sizeof(struct pollfd));

    nat->pollData[0].fd = sock;
    nat->pollData[0].events = POLLIN;

    env->GetJavaVM( &(nat->vm) );
    nat->envVer = env->GetVersion();

    nat->me = env->NewGlobalRef(object);

    nat->javaBuffer = (jbyteArray)(env->NewGlobalRef(buffer));
    nat->bufferSize = (int)size;
    nat->decodedDataSize = 0;

    pthread_create(&(nat->thread), NULL, socketLoopMain, nat);
    result = JNI_TRUE;

done:
    if (JNI_FALSE == result) {
        if (nat->me) env->DeleteGlobalRef(nat->me);
        nat->me = NULL;
        if (nat->pollData) free(nat->pollData);
        nat->pollData = NULL;
    }

    pthread_mutex_unlock(&(nat->thread_mutex));

    return result;
}

static void *socketLoopMain(void *ptr) {

    native_data_t *nat = (native_data_t *)ptr;
    JNIEnv *env;

    JavaVMAttachArgs args;
    char name[] = "SocketLoop";
    args.version = nat->envVer;
    args.name = name;
    args.group = NULL;

    nat->vm->AttachCurrentThread(&env, &args);

    /* For poll result */
    int ret = 0;

    /* For receiving pollin data */
    int rlen;
    char *buffer = (char *)calloc(1, 65536);

    ...

    while ((nat->running)) {

        if ((ret = poll(nat->pollData, 1, -1)) < 0){
            LOGD("In socketLoopMain() : The socket poll error !!!");
            goto close;
        }

            if ((nat->pollData[0].revents & POLLIN)){

                    ...

                rlen = read(nat->pollData[0].fd, buffer, 65536);
                LOGD("In socketLoopMain() : Read bytes = %d", rlen);

                    ...
            }

            else if ((nat->pollData[0].revents & POLLOUT)){
  开发者_如何学C              LOGD("In socketLoopMain() : The socket poll revents [POLLOUT] !!! DO NOTHING");
                continue;
            }
            else if ((nat->pollData[0].revents & POLLERR)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLERR] !!!");
                goto close;
            }
            else if ((nat->pollData[0].revents & POLLHUP)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLHUP] !!!");
                goto close;
            }
            else if ((nat->pollData[0].revents & POLLRDHUP) || (nat->pollData[0].revents & POLLNVAL)){
                LOGD("In socketLoopMain() : The socket poll revents [POLLRDHUP][POLLNVAL] !!!");
                goto close;
            }
    }
    ...
}

Except for first POLLIN, I could not read any data from socket, rlen is always 0.

I build the entire native code to shared lib by using command "make libxxx" in Android source code root directory not "ndk-build".

Any suggestion would be greatly appreciated !!!


After some experiments, I found that (I am not sure that if this is the normal and correct behavior or not...)

(1) In JNI, when client itself close the connected socket, the server would not be aware of that. The server part would receive POLLIN event continuously but if we read the socket, the bytes read would be 0 !!!

(2) In JNI, when server poll socket timeout and close the connected socket, the client would not be aware of that. The client would receive returned value -1 if it tries to send data by this closed socket.

(3) In JNI, if I use the pthread_create and handle the socket, no matter you attach this thread to Java VM or not, the socket would NOT work correctly !!! But if I create the thread in Java instead, and the others remain the same, it works !

That's what I found. Maybe, it is not correct. Please point out. And any suggestion would be greatly appreciated !!!


this page will give you the answer. http://developer.android.com/training/articles/perf-jni.html

  1. you cannot share a JNIEnv between threads.
  2. Threads attached through JNI must call DetachCurrentThread before they exit.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜