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 비트를 실행합니다.
나는 여전히 정확히 같은 방법으로 (다소간)이 문제를 겪고 있습니다. 정말 짜증나. 전체 thread-safetiness가 있고, threading aspect에 대한 나의 지식은 10 배 증가했습니다. 그러나, 나는 아직도이 문제를 겪는다. 스레드와 관련이 없다는 것은 의심 스럽습니다. 모든 변수는 스택에 있습니다. –
코드와 함께 몇 가지 문제가 있는데, 'addrbb'는 공유되어 있으므로 ThreadLocal이 되길 원합니다.. 당신은 C 코드에서 NULL 결과를 검사하지 않는다. (그러나 이것은 사소한 것이어야한다.) 일반적으로 ByteBuffer를 전달하는 표준 방법은 C에서 ByteBuffer.address를'void * '와 같이 처리하는 것이다. 하나 더주의 : ByteBuffer in java 적어도 페이지 크기 (일반적으로 4k)가 할당됩니다. 'ByteBuffer.duplicate'는 같은 기본 배킹 영역에 자바 객체를 생성하기 때문에, 당신의 경우에는 쓸모가 없습니다. 주소 공간을 보존하지 않는다면 ByteBuffer.slice를 사용할 수 있습니다. –
bestsss
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 + 위치; 고칠 수 있지만 더 많은 테스트가 필요합니다. –