2011-09-20 4 views
0

J'écris une méthode native qui peut recevoir des données du socket, puis réécrire dans un ByteArray qui est un paramètre d'entrée de Java. Le socket a été créé dans BlueZ et transmis à mon programme par un message dBus. J'utilise un thread séparé pour faire toute la procédure.Problème de sondage JNI

Merci pour la suggestion de Cerber pour mon précédent problème GetPrimitiveArrayCritical(). Maintenant, le programme pourrait fonctionner sans erreur. Cependant, le nouveau problème est que parce que j'utilise Poll pour attendre l'événement POLLIN, s'il y a des données entrantes disponibles pour la lecture, il y aurait un événement POLLIN théoriquement et je pourrais faire la lecture du socket.

Malheureusement l'événement POLLIN est continuellement déclenché mais je n'ai pas pu lire de données !!! Mais ce comportement étrange ne s'est pas produit quand j'ai fait la même procédure dans le code de BlueZ. Je suis sûr que la prise est correcte.

Le morceau de mon code natif sont quelque chose comme:

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)){ 
       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; 
      } 
    } 
    ... 
} 

Sauf pour la première POLLIN, je ne pouvais pas lire toutes les données de prise, RLEN est toujours 0.

Je construis l'ensemble du code natif à lib partagé en utilisant la commande "make libxxx" dans le répertoire racine du code source Android pas "ndk-build".

Toute suggestion serait grandement appréciée !!!

Répondre

0

Après quelques expériences, je trouve que (je ne suis pas sûr que si tel est le comportement normal et correct ou non ...)

(1) En JNI, lorsque le client lui-même fermer la socket connectée, la le serveur ne serait pas au courant de cela. La partie serveur recevrait l'événement POLLIN en continu mais si nous lisions le socket, les octets lus seraient 0 !!!

(2) Dans JNI, lorsque le serveur interroge le délai d'expiration du socket et ferme le socket connecté, le client n'en est pas conscient. Le client recevrait la valeur -1 retournée s'il essayait d'envoyer des données par ce socket fermé.

(3) Dans JNI, si j'utilise pthread_create et manipule le socket, peu importe que vous attachiez ce thread à Java VM ou non, le socket ne fonctionnerait PAS correctement !!! Mais si je crée le thread en Java à la place, et que les autres restent les mêmes, ça marche!

C'est ce que j'ai trouvé. Peut-être, ce n'est pas correct. S'il vous plaît signaler. Et toute suggestion serait grandement appréciée !!!