2010-04-06 8 views
0

JNI 호출에서 이상한 문자열 손상이 발생하여 Java 측에서 문제가 발생합니다. 모든 경우에 종종 전달 된 배열에서 손상되지 않은 원본 문자열의 기존 부분이있는 손상된 문자열을 가져옵니다. 원래의 C++ 코드는 배열의 첫 번째 인덱스를 주소로 설정해야했습니다. 두 번째 버전은 직접 버퍼를 사용합니다. 문제를 해결하려고했기 때문입니다. 시뮬레이터는 응용 프로그램 스레드와 별도의 스레드에서 실행되며 응용 프로그램 스레드는 실행할 이벤트를 게시합니다.JNI 문자열 손상

내가 전에 버퍼를 미리 할당했기 때문에 여러 스레드가 소켓에 액세스하여 손상을 일으키는 경우 두 번 이상 사용되었을 수 있으므로 할당 된 Mina IoBuffer에 대해 전환했습니다. 풀에서 ByingBuffer를 사용할 수 있습니다. 그러나, 그것은 어떤 차이를 만들지 않은 것 같습니다.

remoteaddress[0]: 10.1.1.2:49153 
remoteaddress[0]: 10.1.4.2:49153 
remoteaddress[0]: 10.1.6.2:49153 
remoteaddress[0]: 10.1.2.2:49153 
remoteaddress[0]: 10.1.9.2:49153 
remoteaddress[0]: {garbage here} 
java.lang.NullPointerException 
    at kokuks.KKSAddress.<init>(KKSAddress.java:139) 
    at kokuks.KKSAddress.createAddress(KKSAddress.java:48) 
    at kokuks.KKSSocket._recvFrom(KKSSocket.java:963) 
    at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:144) 
    at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:1) 
    at kokuks.KKSEvent.run(KKSEvent.java:58) 
    at kokuks.KokuKS.handleJNIEventExpiry(KokuKS.java:872) 
    at kokuks.KokuKS.handleJNIEventExpiry_fjni(KokuKS.java:880) 
    at kokuks.KokuKS.runSimulator_jni(Native Method) 
    at kokuks.KokuKS$1.run(KokuKS.java:773) 
    at java.lang.Thread.run(Thread.java:717) 
remoteaddress[0]: 10.1.7.2:49153 

null 포인터 예외는 손상된 문자열을 사용하려고 시도했을 때 발생합니다. C++에서 주소는 정상적으로 출력되지만, 이렇게하면 오류의 비율이 줄어 듭니다.

는 C++ 코드 :

/* 
* Class:  kokuks_KKSSocket 
* Method: recvFrom2_jni 
* Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIJ)I 
*/ 
JNIEXPORT jint JNICALL Java_kokuks_KKSSocket_recvFrom2_1jni 
(JNIEnv *env, jobject obj, jstring sockpath, jobject addrbuf, jobject buf, jint position, jint limit, jlong flags) { 

    const char* cstr = env->GetStringUTFChars(sockpath, NULL); 
    std::string spath = std::string(cstr); 
    env->ReleaseStringUTFChars(sockpath, cstr); // release me! 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ns3::Ptr<ns3::Object> sockobj = refmap[spath]; 
    ns3::Ptr<ns3::Socket> socket = ns3::DynamicCast<ns3::Socket>(sockobj); 
    if (!socket) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " socket not found for path!!" << std::endl; 
     return -1; // not found 
    } 

    if (!addrbuf) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " sender address directbuffer address is null!" << std::endl; 
     return -1; 
    } 

    uint8_t* bufaddr = (uint8_t*)env->GetDirectBufferAddress(buf); 
    long bufcap = env->GetDirectBufferCapacity(buf); 
    uint8_t* realbufaddr = bufaddr + position; 
    uint32_t remaining = limit - position; 

    uint8_t* addrbufaddr = (uint8_t*)env->GetDirectBufferAddress(addrbuf); 
    long addrbufcap = env->GetDirectBufferCapacity(buf); 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " bufaddr: " << bufaddr << ", cap: " << bufcap << std::endl; 
    } 

    ns3::Address aaddr; 
    uint32_t mflags = flags; 

    int ret = socket->RecvFrom(realbufaddr, remaining, mflags, aaddr); 

    if (ret > 0) { 
     if (KKS_DEBUG) { 
      std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << aaddr << std::endl; 
     } 
     ns3::InetSocketAddress insa = ns3::InetSocketAddress::ConvertFrom(aaddr); 

     std::stringstream ss; 
     insa.GetIpv4().Print(ss); 
     ss << ":" << insa.GetPort() << std::ends; 

     if (KKS_DEBUG) { 
      std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << ss.str() << std::endl; 
     } 

     const char *cstr = ss.str().c_str(); 
     char *dst = (char*)addrbufaddr; 
     size_t len = strlen(cstr); 
     strncpy(dst, cstr, len + 1); 

     if (env->ExceptionOccurred()) { 
      env->ExceptionDescribe(); 
     } 
    } 

    jint jret = ret; 

    return jret; 
} 

/* 
* Class:  kokuks_KKSNode 
* Method: node_getID_jni 
* Signature: (Ljava/lang/String;)I 
*/ 
JNIEXPORT jint JNICALL Java_kokuks_KKSNode_node_1getID_1jni 
(JNIEnv *env, jobject obj, jstring path) { 

    const char* cstr = env->GetStringUTFChars(path, NULL); 
    std::string spath = std::string(cstr); 
    env->ReleaseStringUTFChars(path, cstr); // release me! 

    if (KKS_DEBUG) { 
     std::cout << "[kks-c~" << spath << "?] " << __PRETTY_FUNCTION__ << std::endl; 
    } 

    ns3::Ptr<ns3::Object> nodeobj = refmap[spath]; 
    ns3::Ptr<ns3::Node> node = ns3::DynamicCast<ns3::Node>(nodeobj); 
    if (node) { 
     uint32_t id = node->GetId(); 
     jint  j_id = id; 
     return j_id; 
    } 

    return -1; 
} 

자바 코드 (도움이된다면) :

/** 
* 
* @param remoteaddress 
* @param bytes 
* @param flags 
* @return 
*/ 
protected int _core_recvFrom(final KKSAddress[] remoteaddress, final ByteBuffer bytes, final long flags) throws IOException { 
    if (!kks.isRealtime() || kks.isSimulationThread()) { 
     return _core_recvFrom_st(remoteaddress, bytes, flags); 
    } 

    boolean usejnibb = !bytes.isDirect(); 
    final IoBuffer iob; 

    final ByteBuffer mybuf; 
    if (usejnibb) { 
     if (USE_IOB) { 
      iob = IoBuffer.allocate(bytes.remaining(), true); 
      mybuf = iob.buf(); 
     } else { 
      mybuf = jnibb; 
     } 
     mybuf.clear(); 
     mybuf.limit(bytes.remaining()); 
    } else { 
     mybuf = bytes; 
     iob = null; 
    } 
    try { 
     KKSEvent<Integer> kev = new KKSSocketEvent<Integer>(this) { 
      @Override 
      protected Integer execute(long timeMS) throws IOException { 
       return _core_recvFrom_st(remoteaddress, mybuf, flags); 
      } 

      /* (non-Javadoc) 
      * @see kokuks.KKSEvent#getType() 
      */ 
      public String getType() { 
       return "_core_recvFrom()"; 
      } 
     }; 
     try { 
      int ret = kks.scheduleEventRTWait(kev); 
      if (ret > 0 && usejnibb) { 
       mybuf.flip(); 
       bytes.put(mybuf); 
      } 
      return ret; 
     } catch (InterruptedException e) { 
      throw new InterruptedIOException(); 
     } catch (EventExecException e) { 
      if (e.getCause() instanceof IOException) { 
       throw (IOException)e.getCause(); 
      } 
      throw new IOException(e.getCause()); 
     } catch (Exception e) { 
      throw new IOException(e); 
     } 
    } finally { 
     if (usejnibb) { 
      if (USE_IOB) { 
       iob.free(); 
      } 
     } 
    } 
} 

/** 
* Pass an array of size 1 into remote address, and this will be set with 
* the sender of the packet (hax). This emulates C++ references. 
* 
* @param remoteaddress 
* @param buf 
* @param flags 
* @return 
*/ 
protected int _core_recvFrom_st(final KKSAddress[] remoteaddress, ByteBuffer buf, long flags) throws IOException { 
    try { 
     _syncJNI(); 

     boolean recvfrom = remoteaddress != null; 

     errNo = SocketErrno.ERROR_NOTERROR; 

     ByteBuffer mybuf = buf; 

     if (!buf.isDirect()) { 
      errNo = SocketErrno.ERROR_BUFFERNOTDIRECT; 
      throw new IllegalArgumentException("Buffer not direct!"); 
     } 

     final IoBuffer iob; 
     ByteBuffer bb = null; 
     if (recvfrom) { 
      if (USE_IOB) { 
       iob = IoBuffer.allocate(128, true); 
       bb = iob.buf(); 
      } else { 
       bb = addrbb; 
      } 
      bb.clear(); 
     } else { 
      iob = null; 
     } 

     try { 

      //IoBuffer pre = IoBuffer.wrap(mybuf.duplicate()); 

      //printMessage("sockrecv (pre) // rxavailable: " + getRxAvailable()); 

      // use new mechanism 
      int ret = recvfrom ? 
       recvFrom2_jni(
       path.toPortableString(), 
       bb, 
       mybuf, 
       mybuf.position(), 
       mybuf.limit(), 
       flags 
      ) : recv_jni(
       path.toPortableString(), 
       mybuf, 
       mybuf.position(), 
       mybuf.limit(), 
       flags 
      ); 

      _syncJNI(); 

      if (ret >= 0) { 
       rxTotal += ret; 

       /* 
       printMessage("local addr: " + LOCAL_ADDR + ", real local addr: " + getRemoteAddress().toNormalAddress()); 
       printMessage("remote addr: " + REMOTE_ADDR + ", real remote addr: " + getApp().getNode().getIPV4Address()); 


       if (
        getType() == SOCKET_TYPE_TCP && 
        getRemoteAddress().toNormalAddress().equals(LOCAL_ADDR) && 
        getApp().getNode().getIPV4Address().equals(REMOTE_ADDR) 
       ) { 
        mrTest_testRecvd(mybuf, ret); 
       } 
       */ 

       //printMessage("sockrecv // mybuf: " + mybuf + ", ret: " + ret + " rxavailable: " + getRxAvailable() + ", data: " + BufUtils.asText(mybuf, ret)); 

       buf.position(buf.position() + ret); 

       if (recvfrom) { 
        String st; 
        try { 
         st = IoBuffer.wrap(bb).getString(CDE); 
         remoteaddress[0] = KKSAddress.createAddress(st); 
         if (remoteaddress[0] == null) { 
          System.out.println("warning; remote address is null!! original: " + st); 
         } 
        } catch (CharacterCodingException e) { 
         e.printStackTrace(); 
        } 
       } 

       return ret; 

       //pre.limit(pre.position() + ret); 
       //printMessage("_core_recvFrom_st recvd from " + ((!recvfrom || remoteaddress[0] == null || remoteaddress == null) ? getRemoteAddress() : remoteaddress[0]) + ": " + pre.getHexDump()); 
      } 
      throw new IOException("I/O exception, retval: " + ret + ", errNo: " + errNo); 
     } finally { 
      if (recvfrom) { 
       if (USE_IOB) { 
        iob.free(); 
       } 
      } 
     } 
    } finally { 
     errNo = _getErrNo(); 
    } 
} 

편집 : 나는 또한 패킷 데이터가 손상지고 것을 확인했다. 또한이 문제는 제 집 PC보다 제 집 PC에서 더 많이 발생합니다. 내 작업 PC는 Windows XP를 실행하고 RAM이 4GB이고 Q6600을 가지고 있으며 가정용 PC는 Q6600, 4GB RAM을 오버 클럭하고 32 비트 Java를 사용하지만 Windows 7 64 비트를 실행합니다.

+0

나는 여전히 정확히 같은 방법으로 (다소간)이 문제를 겪고 있습니다. 정말 짜증나. 전체 thread-safetiness가 있고, threading aspect에 대한 나의 지식은 10 배 증가했습니다. 그러나, 나는 아직도이 문제를 겪는다. 스레드와 관련이 없다는 것은 의심 스럽습니다. 모든 변수는 스택에 있습니다. –

+0

코드와 함께 몇 가지 문제가 있는데, 'addrbb'는 공유되어 있으므로 ThreadLocal이 되길 원합니다. . 당신은 C 코드에서 NULL 결과를 검사하지 않는다. (그러나 이것은 사소한 것이어야한다.) 일반적으로 ByteBuffer를 전달하는 표준 방법은 C에서 ByteBuffer.address를'void * '와 같이 처리하는 것이다. 하나 더주의 : ByteBuffer in java 적어도 페이지 크기 (일반적으로 4k)가 할당됩니다. 'ByteBuffer.duplicate'는 같은 기본 배킹 영역에 자바 객체를 생성하기 때문에, 당신의 경우에는 쓸모가 없습니다. 주소 공간을 보존하지 않는다면 ByteBuffer.slice를 사용할 수 있습니다. – bestsss

+0

IoBuffer는 ThreadLocal ByteBuffer 할당 풀을 사용합니다 :) 문제는 다른 행에서 변환하는 대신 Java 값을 직접 사용했을 수도 있습니다. long bufcap = env-> GetDirectBufferCapacity (buf); uint8_t * realbufaddr = bufaddr + 위치; 나는 한 줄로 해보려 고 시도한 과거의 문제를 보았습니다. 그러나 앱은 실패합니다 (대부분 반환 값을 위해). jlong ​​j_bufcap = env-> GetDirectBufferCapacity (buf); long bufcap = j_bufcap; uint8_t * realbufaddr = bufaddr + 위치; 고칠 수 있지만 더 많은 테스트가 필요합니다. –

답변

0

귀하의 코드를 테스트하지는 않았지만 문자 인코딩 문제로 가정합니다. std::string 또는 이와 비슷한 것을 std::wstring 대신 사용해보세요. 죄송합니다. 지금은별로 도움이되지 않지만 시작점이되어야합니다.

+0

확실하지 않은 이유는 항상 모든 시간보다 가끔씩 가비지 문자열을 줄 것입니다. 아마도 그것은 메모리 할당 문제 일 것입니다. 매우 이상합니다. –

0

스레드 안전 문제 일 수 없었습니까? 네이티브 메소드에 대한 액세스를 동기화하십시오.

+0

이것으로 해결할 수 있다고 생각 합니다만, JNI는 스레드로부터 안전하다고 생각하지 않습니까? –

+0

JNI가 thread safe 한 것인지 정말로 모르겠지만 C++ DLL이 아닌 경우에는 전체 솔루션이 스레드로부터 안전하지 않습니다. – Juliano

+0

음, 음, C++ DLL은 스레드로부터 안전해야하며, 자바 측에서 호출되는 이벤트 함수에 뮤텍스가 있습니다. 나는 그 문제를 일으킬 수있는 JNI 메커니즘을 떠난다 고 생각한다. –