JNI 轮询问题
我编写了一个本机方法,可以从套接字接收数据,然后写回 ByteArray,这是来自 Java 的输入参数。该套接字是在 BlueZ 中创建的,并通过 dBus 消息传输到我的程序。我使用一个单独的线程来完成整个过程。
感谢 Cerber 对我之前的 GetPrimitiveArrayCritical() 问题的建议。现在,程序可以正常运行了。
然而,新的问题是,因为我使用 Poll 来等待 POLLIN 事件,所以如果有传入的数据可供读取,理论上就会有 POLLIN 事件,我可以进行套接字读取。
不幸的是,POLLIN 事件不断触发,但我无法读取任何数据! 但是当我在 BlueZ 的代码中执行完全相同的过程时,这种奇怪的行为并没有发生。 我确信插座是正确的。
我的本机代码如下:
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;
}
}
...
}
除了第一个 POLLIN,我无法从套接字读取任何数据,rlen 始终为 0。
我通过在 Android 源代码根目录中使用命令“make libxxx”将整个本机代码构建到共享库目录不是“ndk-build”。
任何建议将不胜感激!
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)){
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 !!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
经过一些实验,我发现(我不确定这是否是正常且正确的行为......)
(1)在JNI中,当客户端本身关闭连接的套接字时,服务器不会意识到这一点。服务器部分将连续接收 POLLIN 事件,但如果我们读取套接字,则读取的字节将为 0!
(2) 在JNI中,当服务器轮询套接字超时并关闭连接的套接字时,客户端不会意识到这一点。如果客户端尝试通过此关闭的套接字发送数据,它将收到返回值 -1。
(3) 在JNI中,如果我使用pthread_create并处理套接字,无论您是否将此线程附加到Java VM,套接字都将无法正常工作!但如果我用 Java 创建线程,并且其他线程保持不变,它就可以工作了!
这就是我发现的。或许,这并不正确。请指出。
任何建议将不胜感激!
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 !!!
这个页面会给你答案。
http://developer.android.com/training/articles/perf-jni.html
this page will give you the answer.
http://developer.android.com/training/articles/perf-jni.html