2010-04-06 170 views
0

我在JNI調用中收到了導致Java端出現問題的奇怪字符串損壞。每隔一段時間,我都會在傳遞的數組中獲取一個損壞的字符串,該字符串有時具有原始未損壞字符串的現有部分。最初的C++代碼應該將數組的第一個索引設置爲地址。第二個版本使用直接緩衝區,因爲我試圖解決這個問題。模擬器在與應用程序線程分開的線程中運行,應用程序線程發佈要執行的事件。JNI字符串損壞

我之前也想過,因爲我預先分配了緩衝區,所以如果多個線程訪問套接字並導致損壞,那麼它可能會被多次使用,所以我將它轉換爲分配的Mina IoBuffer從一個池中提供ByteBuffer支持。但是,它似乎沒有任何區別。

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 

空指針異常來自嘗試使用損壞的字符串。在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; 
} 

的Java代碼(如果它幫助):

/** 
* 
* @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(); 
    } 
} 

編輯:我還確定了分組數據是越來越損壞。此外,我的工作電腦上發生的問題比我的家用電腦多。我的工作電腦運行Windows XP,擁有4GB內存和Q6600,我的家用電腦擁有超頻Q6600,4GB內存,並且運行Windows 7 64位,儘管它採用32位Java。

+0

我仍然得到這個問題,一年後,在完全相同的方法(或多或少)。真的很煩人。有完整的線程安全性,並且我對線程方面的知識增加了十倍。但是,我仍然遇到這個問題。我懷疑這與線程有關。所有變量都在堆棧中。 – 2011-05-16 18:52:49

+0

我在代碼中看到了很多問題,「addrbb」是共享的,所以你希望它是一個ThreadLocal 。你不檢查C代碼中的NULL結果(但應該是小代碼),通常ByteBuffer的標準方法是在C中處理ByteBuffer.address,如'void *'。另外一個注意事項:java中的ByteBuffer是至少分配了一個頁面大小(通常是4k)。 'ByteBuffer.duplicate'只是在相同的底層支持區域創建java對象,所以在你的情況下它是無用的,如果你不想節省地址空間,你可以使用ByteBuffer.slice。 – bestsss 2011-05-17 07:08:50

+0

IoBuffer使用ThreadLocal ByteBuffer分配池:)我認爲問題可能是直接使用Java值,而不是在不同的行上進行轉換。 long bufcap = env-> GetDirectBufferCapacity(buf); uint8_t * realbufaddr = bufaddr + position; 我在過去曾經遇到過一些問題,我嘗試過在單行上做,而且應用程序失敗(主要是返回值)。 jlong​​ j_bufcap = env-> GetDirectBufferCapacity(buf); long bufcap = j_bufcap; uint8_t * realbufaddr = bufaddr + position; 可能已經修復它,但需要更多的測試。 – 2011-05-17 16:53:45

回答

0

我沒有測試過你的代碼,但我假設它是一個字符編碼問題。嘗試使用std::wstring而不是std::string或類似的東西。對不起現在幫不了多少忙,但是這應該給你一個起點,我想。

+0

不知道,爲什麼它每隔一段時間都會給我垃圾字符串,而不是所有的時間?也許這是一個內存分配問題。很奇怪。 – 2010-04-07 13:04:51

0

不可能是線程安全問題嗎?嘗試將訪問權限同步到本地方法。

+0

我認爲這確實把它整理出來,但不是JNI應該是線程安全的? – 2010-04-12 13:50:06

+0

我真的不知道JNI是否是線程安全的,但如果C++ DLL不是,整個解決方案將不會是線程安全的。 – Juliano 2010-04-12 14:15:15

+0

嗯,C++ DLL應該是線程安全的,在其中包含的事件函數中有一個互斥體,這些互斥體是從Java端調用的。我想只留下可能有問題的JNI機制。 – 2010-04-12 17:31:42