JNI字符串腐败

时间:2010-04-06 17:45:25

标签: java c++ multithreading java-native-interface

我在JNI调用中遇到了奇怪的字符串损坏,这导致了Java端的问题。每隔一段时间,我就会在传递的数组中得到一个损坏的字符串,它有时会包含原始未损坏字符串的现有部分。原始的C ++代码应该将数组的第一个索引设置为地址。第二个版本使用直接缓冲区,因为我试图解决问题。模拟器在与应用程序线程分开的线程中运行,应用程序线程发布要执行的事件。

我之前想过,因为我正在预先分配缓冲区,如果多个线程访问套接字,它可能会被多次使用,导致损坏,所以我将其切换为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();
    }
}

编辑:我还发现数据包数据已损坏。此外,问题发生在我的工作PC上,而不是我的家用电脑。我的PC运行Windows XP,有4GB内存并有Q6600,我的家用PC有超频的Q6600,4GB内存,并运行Windows 7 64位,虽然它是32位Java。

2 个答案:

答案 0 :(得分:0)

我没有测试过您的代码,但我认为这是一个字符编码问题。尝试使用std::wstring代替std::string或类似的东西。对不起,此刻此刻无济于事,但我想这应该给你一个起点。

答案 1 :(得分:0)

不能是线程安全问题吗?尝试同步对本机方法的访问。