jicmp 添加 ping 超时
把 jicmp 中 src 目录下的代码 copy 新建一个 maven 项目
将编译后的 jicmp 下的 pom 文件内容复制过来
IcmpSocket 只加载一次 lib
将加载 lib 的代码移动到静态代码块里,原来是 new 的时候每次都处理
static {
String property = System.getProperty(PROPERTY_NAME);
boolean loaded = false;
if (property != null) {
log().debug("System property '" + PROPERTY_NAME + "' set to '" + property + ". Attempting to load " + LIBRARY_NAME + " library from this location.");
try {
System.load(property);
loaded = true;
} catch (final UnsatisfiedLinkError e) {
log().info("Failed to load library " + property + ".");
}
}
if (!loaded) {
log().debug("Attempting to load library using System.loadLibrary(\"" + LIBRARY_NAME + "\").");
System.loadLibrary(LIBRARY_NAME);
}
log().debug("Successfully loaded " + LIBRARY_NAME + " library.");
}
超时 receive
我打算超时时抛出 SocketTimeoutException
/**
* This method is used to receive the next ICMP datagram from the operating
* system. The returned datagram packet's address is set to the sending
* host's address. The port number is always set to Zero, and the buffer is
* set to the contents of the raw ICMP message.
*
* @param timeout 读取超时时间,ms
* @throws IOException Thrown if an error occurs reading the next ICMP message.
*/
public native DatagramPacket receive(int timeout) throws IOException, SocketTimeoutException;
生成 jni 头文件代码
cd 到 src\main\java 目录下
javah -classpath . org.opennms.protocols.icmp.IcmpSocket
会在当前目录生成 org_opennms_protocols_icmp_IcmpSocket.h,复制生成新增的 C 方法声明
/*
* Class: org_opennms_protocols_icmp_IcmpSocket
* Method: receive
* Signature: (I)Ljava/net/DatagramPacket;
*/
JNIEXPORT jobject JNICALL Java_org_opennms_protocols_icmp_IcmpSocket_receive__I
(JNIEnv *, jobject, jint);
C 代码改动
相应方法声明放到 C++ 项目 org_opennms_protocols_icmp_IcmpSocket.h 头文件里
IcmpSocket.c 改动代码
第一个方法上面声明一下抽出来的函数
jobject receive(JNIEnv*, jobject, jint);
/*
* Class: org_opennms_protocols_icmp_IcmpSocket
* Method: receive
* Signature: ()Ljava/net/DatagramPacket;
*/
JNIEXPORT jobject JNICALL
Java_org_opennms_protocols_icmp_IcmpSocket_receive (JNIEnv *env, jobject instance)
{
return receive(env, instance, 0);
}
/*
* Class: org_opennms_protocols_icmp_IcmpSocket
* Method: receive
* Signature: (I)Ljava/net/DatagramPacket;
*/
JNIEXPORT jobject JNICALL Java_org_opennms_protocols_icmp_IcmpSocket_receive__I
(JNIEnv* env, jobject instance, jint timeout)
{
return receive(env, instance, timeout);
}
将原来的 receive 和带超时时间的 receive 同时调用 receive,原来的代码移动到抽取出的方法里,然后在 recvfrom 之前设置超时时间参数
然后在本身就有的 SOCKET_ERROR 错误处理里面单独处理超时错误
setsockopt(fd_value, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(jint));
超时设置,本来使用结构体 TIMEVAL,设置秒和微秒字段,但是发现不行,不知道为啥。
// receive timeout set
if (timeout > 0)
{
iRC = setsockopt(fd_value, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(jint));
if (iRC == SOCKET_ERROR)
{
// error set socket config
char errBuf[256];
int savedErrno = errno;
snprintf(errBuf, sizeof(errBuf), "Error set receive timeout (iRC = %d, fd_value = %d, %d, %s)", iRC, fd_value, savedErrno, strerror(savedErrno));
throwError(env, "java/io/IOException", errBuf);
goto end_recv;
}
}
/**
* Receive the data from the operating system. This
* will also include the IP header that precedes
* the ICMP data, we'll strip that off later.
*/
iRC = recvfrom(fd_value, inBuf, MAX_PACKET, 0, (struct sockaddr*)&inAddr, &inAddrLen);
// 出错
if (iRC == SOCKET_ERROR)
{
int savedErrno = errno;
char errBuf[256];
if (timeout > 0)
{
// 超时错误
if (savedErrno == WSAETIMEDOUT)
{
// 错误信息复制原来的,一般应该实际不会暴露出详细信息
snprintf(errBuf, sizeof(errBuf), "Reading data timeout (iRC = %d, fd_value = %d, %d, %s)", iRC, fd_value, savedErrno, strerror(savedErrno));
// 抛出超时异常
throwError(env, "java/net/SocketTimeoutException", errBuf);
goto end_recv;
}
}
/*
* Error reading the information from the socket
*/
snprintf(errBuf, sizeof(errBuf), "Error reading data from the socket descriptor (iRC = %d, fd_value = %d, %d, %s)", iRC, fd_value, savedErrno, strerror(savedErrno));
throwError(env, "java/io/IOException", errBuf);
goto end_recv;
}
问题:如果我不删掉 C 里面原来的 Java_org_opennms_protocols_icmp_IcmpSocket_receive 方法,则无论 Java 是否传参都会调用它,而我删掉这个方法时才会显式调用有参方法
解决:经过对比发现,新增的方法是重载原来的,于是原来的那个 C 的方法名也变了,Java_org_opennms_protocols_icmp_IcmpSocket_receive -> Java_org_opennms_protocols_icmp_IcmpSocket_receive__
于是在 Win 上终于可以了