android 把socket 提到JNI层实现

来源:互联网 发布:众银家苹果下载软件 编辑:程序博客网 时间:2024/06/03 20:00

设计背景:

1. APP 需要不断与中控系统进行数据包通信,设计分包,重发 机制,负荷压力大,在JAVA层实现,效率慢,于是提到JNI 层实现.

JAVA

private native void initUDPSocket(int port);private native String getLocalSocketIP();private native void sendData(String ip, int port, byte[] data, int len);private native void freeSocket();

2.JNI:

//MAX data buffer size#define MAX_BUFFER_SIZE 1024static void BindSocketToPort(JNIEnv* env, jobject obj, int sd, unsigned short port);        void* ReceiveFromSocket(void* args);static int m_udpSocket;static int m_isExit = 0;static JavaVM* gVm = NULL;static jobject gObj = NULL;static jmethodID gOnReceiveData = NULL;char* TAG= "NATIVE_UDPMANAGER";jint JNI_OnLoad(JavaVM* vm, void* reserved){   gVm = vm;   return JNI_VERSION_1_4;}//初始化JNIEXPORT void JNICALL Java_com_controlService_UDPManager_initUDPSocket  (JNIEnv * env, jobject obj, jint port){   if(NULL == gObj){      gObj = (*env)->NewGlobalRef(env,obj);      if(NULL == gObj)         return;   }   if(NULL == gOnReceiveData){      jclass clazz = (*env)->GetObjectClass(env,obj);      gOnReceiveData = (*env)->GetMethodID(env,clazz,"onReceiveData","(Ljava/lang/String;I[B)V");        (*env)->DeleteLocalRef(env,clazz);      if(NULL == gOnReceiveData)          __android_log_print(ANDROID_LOG_ERROR, TAG,"GetMethodID error");   }   __android_log_print(ANDROID_LOG_INFO, TAG,"Constructing a new UDP socket...");    m_udpSocket = socket(PF_INET, SOCK_DGRAM,0);    if( -1 == m_udpSocket){       ThrowErrnoException(env,"java/io/IOException",errno);    }    BindSocketToPort(env,obj,m_udpSocket,(unsigned short)port);    //线程句柄    pthread_t thread;    int result = pthread_create(&thread,NULL,ReceiveFromSocket,NULL);    if(0 != result){       jclass ex = (*env)->FindClass(env,"java/lang/RuntimeException");       (*env)->ThrowNew(env,ex,"Unable to create thread");    }}JNIEXPORT void JNICALL Java_com_controlService_UDPManager_sendData(JNIEnv *env, jobject obj, jstring ip, jint port, jbyteArray data, jint len){    struct sockaddr_in address;    memset(&address, 0, sizeof(address));    address.sin_family = PF_INET;    //以C字符串形式获取IP地址    const char* ipAddress = (*env)->GetStringUTFChars(env,ip, NULL);    if(NULL == ipAddress){       __android_log_print(ANDROID_LOG_ERROR, TAG,"wrong ip..");       return;    }    //将IP地址字符串转换为网络地址    int result = inet_aton(ipAddress, &(address.sin_addr));    //释放IP地址    (*env)->ReleaseStringUTFChars(env,ip, ipAddress);    //如果转换失败    if( 0 == result){       ThrowErrnoException(env,"java/io/IOException", errno);       return;    }    //将端口转换转换为网络字节顺序    address.sin_port = htons(port);    //将本地指针指向含有Java端数组的内存地址,依赖JVM的具体实现    //可能是锁住java端的数组不被回收(增加引用计数),也可能在jvm所在的堆上拷贝一份    //速度和效率比GetByteArrayRegion方法高很多    unsigned char * cdata = (*env)->GetByteArrayElements(env, data, NULL);    if(NULL == cdata){       __android_log_print(ANDROID_LOG_ERROR, TAG,"GetByteArrayElements failed...");       return;    }    ssize_t sendSize = sendto(m_udpSocket, cdata, (size_t)len, 0, &address, sizeof(struct sockaddr_in));    (*env)->ReleaseByteArrayElements(env,data,cdata,0);    if( -1 == sendSize){       ThrowErrnoException(env,"java/io/IOException", errno);    }}JNIEXPORT void JNICALL Java_com_controlService_UDPManager_freeSocket  (JNIEnv * env, jobject obj){    if(NULL != gObj)    {       (*env)->DeleteGlobalRef(env,gObj);       gObj = NULL;    }    m_isExit = 1;    close(m_udpSocket);}static void BindSocketToPort(JNIEnv* env, jobject obj, int sd,        unsigned short port) {    struct sockaddr_in address;    //清空结构体    memset(&address, 0, sizeof(address));    address.sin_family = PF_INET;    //Bind to all address    address.sin_addr.s_addr = htonl(INADDR_ANY);    //Convert port to network byte order    address.sin_port = htons(port);    //Bind socket    __android_log_print(ANDROID_LOG_INFO, TAG,"bind port:%d",port);    // 设置套接字选项避免地址使用错误   int on=1;   if((setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)   {      __android_log_print(ANDROID_LOG_ERROR, TAG,"setsockopt failed");      exit(EXIT_FAILURE);   }    //sockaddr方便函数传递, sockaddr_in方便用户设定, 所以需要的时候在这2者之间进行转换    if (-1 == bind(sd, (struct sockaddr*) &address, sizeof(address))) {        ThrowErrnoException(env, "java/io/IOException", errno);    }    int broadcastEnable=1;    int ret=setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));   if(ret < 0)   {      __android_log_print(ANDROID_LOG_ERROR, TAG,"ERROR BROADCASTING, Coulnt set broadcast enable through socket options");   }}JNIEXPORT jstring JNICALL Java_com_controlService_UDPManager_getLocalSocketIP  (JNIEnv *env, jobject obj){   jstring jip = NULL;   struct sockaddr_in address;    socklen_t addressLength = sizeof(address);   if (-1 == getsockname(m_udpSocket, (struct sockaddr*) &address, &addressLength)) {      ThrowErrnoException(env, "java/io/IOException", errno);   } else {      char *ip = inet_ntoa(address.sin_addr);      jip =(*env)->NewStringUTF(env,ip);   }   return jip;}//接收 recv()void* ReceiveFromSocket(void* args) {   JNIEnv* env = NULL;   if(0 == (*gVm)->AttachCurrentThread(gVm,&env,NULL)){      unsigned char buffer[MAX_BUFFER_SIZE];      //客户端地址      struct sockaddr_in address;      while(!m_isExit){         memset(&address,0, sizeof(address));         socklen_t addressLen = sizeof(struct sockaddr_in);         ssize_t recvSize = recvfrom(m_udpSocket, buffer, MAX_BUFFER_SIZE - 1, 0,&address,&addressLen);         if (-1 == recvSize) {            ThrowErrnoException(env, "java/io/IOException", errno);         } else if(recvSize != 0){            __android_log_print(ANDROID_LOG_INFO, TAG,"received data...");            //字符串截断            buffer[recvSize] = '0';            //分配            jbyteArray array = (*env)->NewByteArray(env,recvSize);            (*env)->SetByteArrayRegion(env,array,0,recvSize,buffer);            char *ip = inet_ntoa(address.sin_addr);            __android_log_print(ANDROID_LOG_INFO, TAG,"received data from %s",ip);                jstring jip =(*env)->NewStringUTF(env,ip);                if(NULL == jip){                  __android_log_print(ANDROID_LOG_ERROR, TAG,"could not new java string..");                }                jint port = ntohs(address.sin_port);            (*env)->CallVoidMethod(env,gObj,gOnReceiveData,jip,port,array);            (*env)->DeleteLocalRef(env,array);            (*env)->DeleteLocalRef(env,jip);         }      }      (*gVm)->DetachCurrentThread(gVm);   }    return (void*)1;}


0 0