JNI使用实例之C、C++ DLL回调java成员函数

时间:2021-02-01 18:15:54

使用场景描述如下:

1、使用java程序调用C开发的DLL,传入一个已封装的socket对象

2、在C开发的DLL中根据传入的对象调用java提供的recv()、send()函数发送数据

java部分对应的代码如下:

// ISocketBase.java
package com.tms;
public interface ISocketBase {
	public void send(byte [] data);
	public byte [] recv(int timeout);
}
// SocketImp.java
package com.tms;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.charset.*;

public class SocketImp implements  ISocketBase{
    private ServerSocket server;
    private Socket socket;

    private OutputStream dos;
    private InputStream dis;
    
    public SocketImp() {
    }
    
    public void EndSocket() {
        try {
            dis.close();
            dos.close();
            socket.close();
            server.close();
        } catch (IOException e) {
        }
    }
    
    public void StartSocket() {
        try {
            server = new ServerSocket(8888);
            System.out.println("start server connetion, port = 8888.");
            socket = server.accept();
            
            dos = socket.getOutputStream();
            dis = socket.getInputStream();
        } catch (SocketException e) {
            System.out.println("The network connection is exception, the program exits.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void printHexString(byte[] b, int iLen)
    {
        for (int i = 0; i < iLen; i++)
        {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1)
            {
                hex = '0' + hex;
            }
            System.out.print(hex.toUpperCase() + " ");
        }
        System.out.println("");
    }
    
    @Override
    public void send(byte[] bytes) {
        try {
            dos.write(bytes);
            System.out.println("[java send] length = " + bytes.length + ".");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public byte[] recv(int timeout) {
        byte[] cRecvData = new byte[1024 * 3];
        int iLength = 0;
        
        try {
            socket.setSoTimeout(timeout);
            iLength = dis.read(cRecvData);
            printHexString(cRecvData, iLength);
            System.out.println("java read length: " + iLength);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        byte[] cRet = new byte[iLength];
        for(int i = 0; i < iLength; ++i) {
            cRet[i] = cRecvData[i];
        }
        return cRet;
    }
}
// CallDllNative.java
package com.tms;

import java.io.*;

public class CallDllNative {
    
    static{
        System.loadLibrary("C_DLL_NAME");
    }
    
    public native static void Download(ISocketBase socketBase, String binFile, StringBuffer retCode);
    
    public static void main(String args[]) {
        String binFile = new String("./123.txt");
        SocketImp socket;
        StringBuffer retCode = new StringBuffer("23");
        
        socket = new SocketImp();
        socket.StartSocket();
        
        System.out.println("CallDllNative is running... ...");
        try
        {
            Download(socket, binFile, retCode);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        
        System.out.println("retCode = " + retCode);
        
        socket.EndSocket();
    }
}

c部分对应的代码如下:

// Download.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_tms_CallDllNative */

#ifndef _Included_com_tms_CallDllNative 
#define _Included_com_tms_CallDllNative

extern int SendData(char* pSendData, int iSendDataLen);
extern int RecvData(char* pRecvData, int iRecvDataLen, int timeout);

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_tms_CallDllNative
 * Method:    Download
 * Signature: (Lcom/tms/ISocketBase;Ljava/lang/String;Ljava/lang/StringBuffer;)V
 */
JNIEXPORT void JNICALL Java_com_tms_CallDllNative_Download
  (JNIEnv *, jclass, jobject, jstring, jobject);

#ifdef __cplusplus
}
#endif
#endif

// Download.c
#include <stdio.h>
#include <string.h>

#include "Download.h"

static JNIEnv*        s_env;
static jobject        s_socketBase;
static jclass        s_clsSocket;

extern int Download(const char* pFilePath);

int SendData(char* pSendData, int iSendDataLen)
{
    jmethodID sendFun = (*s_env)->GetMethodID(s_env, s_clsSocket, "send", "([B)V");
    jbyteArray pSend = (*s_env)->NewByteArray(s_env, iSendDataLen);

    if(NULL == sendFun || NULL == pSend) {
        printf("NULL == sendFun || NULL == pSend.\n");
        return -1;    // OutOfMemoryError already thrown
    }
    
    (*s_env)->SetByteArrayRegion(s_env, pSend, 0, iSendDataLen, (jbyte*)pSendData);
    
    (*s_env)->CallVoidMethod(s_env, s_socketBase, sendFun, pSend);
    
    (*s_env)->ReleaseByteArrayElements(s_env, pSend, (*s_env)->GetByteArrayElements(s_env, pSend, NULL), 0);

    return iSendDataLen;
}


int RecvData(char* pRecvData, int iRecvDataLen, int timeout)
{
    jbyteArray  jRecvArray;
    jbyte*      jRecv;
    int         iRecvLen;
    jmethodID   recvFun = (*s_env)->GetMethodID(s_env, s_clsSocket, "recv", "(I)[B");

    jRecvArray = (jbyteArray)(*s_env)->CallObjectMethod(s_env, s_socketBase, recvFun, timeout);
    if(NULL == jRecvArray) {
        printf("NULL == jRecvArray.\n");
        return -1;
    }
    
    jRecv = (*s_env)->GetByteArrayElements(s_env, jRecvArray, 0);
    if(NULL == jRecv) {
        printf("NULL == jRecv.\n");
        return -1;
    }
    iRecvLen = (*s_env)->GetArrayLength(s_env, jRecvArray);
    if(iRecvLen > iRecvDataLen) {
        iRecvLen = iRecvDataLen;
    }
    memcpy(pRecvData, jRecv, iRecvLen);
    (*s_env)->ReleaseByteArrayElements(s_env, jRecvArray, jRecv, 0);

    return iRecvLen;
}


static int SetRetValue(int iRetValue, jobject retCode)
{
    char      cRetCode[20];
    int       iLen = 0;
    jstring   strRetCode;

    jclass    clsRetCode    = (*s_env)->GetObjectClass(s_env, retCode);
    jmethodID lenFunID    = (*s_env)->GetMethodID(s_env, clsRetCode, "length", "()I");
    jmethodID deleteFunID    = (*s_env)->GetMethodID(s_env, clsRetCode, "delete", "(II)Ljava/lang/StringBuffer;");
    jmethodID appendFunID    = (*s_env)->GetMethodID(s_env, clsRetCode, "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");

    printf("iRetValue = %d\n", iRetValue);

    if(iRetValue < 0)
    {
        iRetValue = -iRetValue;
    }

    cRetCode[0] = iRetValue / 10 + '0';
    cRetCode[1] = iRetValue % 10 + '0';
    cRetCode[2] = '\0';
    strRetCode    = (*s_env)->NewStringUTF(s_env, cRetCode);

    if(NULL == clsRetCode || NULL == lenFunID || NULL == deleteFunID
        || NULL == appendFunID || NULL == strRetCode) {
        printf("SetRetValue == NULL.\n");
        return -1;
    }

    iLen = (*s_env)->CallIntMethod(s_env, retCode, lenFunID);
    (*s_env)->CallObjectMethod(s_env, retCode, deleteFunID, 0, iLen);
    (*s_env)->CallObjectMethod(s_env, retCode, appendFunID, strRetCode);

    (*s_env)->DeleteLocalRef(s_env, strRetCode);

    return 0;
}


/*
 * Class:     com_tms_DownloadNative
 * Method:    tmsDownload
 * Signature: (Lcom/tms/ISocketBase;Ljava/lang/String;Ljava/lang/StringBuffer;)V
 */
JNIEXPORT void JNICALL Java_com_tms_CallDllNative_Download
  (JNIEnv* env, jclass cls, jobject socketBase, jstring binFile, jobject retCode)
{
    int iRet = 0;
    const char *pFilePath = (*env)->GetStringUTFChars(env, binFile, NULL);
    
    s_clsSocket = (*env)->GetObjectClass(env, socketBase);
    if(NULL == pFilePath || NULL == s_clsSocket) {
        printf("NULL == pFilePath || NULL == s_clsSocket.\n");
        return ;
    }
    
    s_env = env;
    s_socketBase = socketBase;
    
    iRet = Download(pFilePath);
    
    if(0 != SetRetValue(iRet, retCode)) {
        printf("Failed to SetRetValue().\n");
        return ;
    }
    
    (*env)->ReleaseStringUTFChars(env, binFile, pFilePath);
}

对应的java编译命令如下:

javac -d . com\tms\*.java
java -Djava.library.path=. com.tms.CallDllNative