当前位置: 首页 > news >正文

【JNA】JAVA使用JNA调用C++ dll文件(2)JNA 对接代理DLL

上面的文章已经实现了C++代理层,封装了代理层的头文件 CTPProxy.h,下面就使用JNA对接这个代理DLL文件 CTPProxy.dll

JNA 对接步骤

1、添加 JNA 依赖

在 Maven 项目的 pom.xml 中添加依赖:

<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.13.0</version> <!-- 最新版本可到 Maven 仓库查询 -->
</dependency>

2、将 CTPProxy.dll和CTP的原始文件一起放在工程中

在这里插入图片描述

3、创建代理DLL接口映射

实现的函数和CTPProxy.dll暴露的函数要完全匹配

import com.sun.jna.*;
// 代理DLL接口映射
public interface CTPMdApi extends Library {CTPMdApi INSTANCE = Native.load("CTPProxy01", CTPMdApi.class);// 创建行情API实例Pointer CreateMdApi(String pszFlowPath, boolean bIsUsingUdp, boolean bIsMulticast);// 初始化API实例void MdApi_Init(Pointer api);// 释放API实例资源void MdApi_Release(Pointer api);// 注册回调接口实例void MdApi_RegisterSpi(Pointer api, Pointer spi);// 注册前置机地址int MdApi_RegisterFront(Pointer api, String frontAddress);// 注册名字服务器地址void MdApi_RegisterNameServer(Pointer api, String nsAddress);// 发起用户登录请求String MdApi_ReqUserLogin(Pointer api, String brokerId, String userId, String password, int requestId);// 发起用户登出请求int MdApi_ReqUserLogout(Pointer api, String brokerId, String userId, int requestId);// 订阅行情数据int MdApi_SubscribeMarketData(Pointer api, String[] instrumentIds, int count);// 取消订阅行情数据int MdApi_UnSubscribeMarketData(Pointer api, String[] instrumentIds, int count);// 订阅询价数据int MdApi_SubscribeForQuoteRsp(Pointer api, String[] instrumentIds, int count);// 取消订阅询价数据int MdApi_UnSubscribeForQuoteRsp(Pointer api, String[] instrumentIds, int count);// 获取API版本号String MdApi_GetApiVersion();// 创建行情回调实例Pointer CreateMdSpi(CTPMdCallbacks callbacks);// 释放行情回调实例void ReleaseMdSpi(Pointer spi);
}

4、创建回调结构体(对应C的MdSpiCallbacks)


import com.sun.jna.Callback;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;import java.util.Arrays;
import java.util.List;// 回调结构体(对应C的MdSpiCallbacks)
public class CTPMdCallbacks extends Structure {// 回调函数接口定义// 前置机连接成功public interface OnFrontConnectedCallback extends Callback {void invoke(Pointer userData);}// 前置机断开连接public interface OnFrontDisconnectedCallback extends Callback {void invoke(Pointer userData, int nReason);}// 心跳超时警告public interface OnHeartBeatWarningCallback extends Callback {void invoke(Pointer userData, int nTimeLapse);}// 登录响应public interface OnRspUserLoginCallback extends Callback {void invoke(Pointer userData, String errorMsg, int errorId);}public interface OnRspUserLogoutCallback extends Callback {void invoke(Pointer userData, String errorMsg, int errorId);}// 订阅行情响应public interface OnRspSubMarketDataCallback extends Callback {void invoke(Pointer userData, String instrumentId, String errorMsg, int errorId, boolean bIsLast);}// 取消订阅行情响应public interface OnRspUnSubMarketDataCallback extends Callback {void invoke(Pointer userData, String instrumentId, String errorMsg, int errorId, boolean bIsLast);}// 取消订阅询价响应public interface OnRspSubForQuoteRspCallback extends Callback {void invoke(Pointer userData, String instrumentId, String errorMsg, int errorId, boolean bIsLast);}// 取消订阅询价响应public interface OnRspUnSubForQuoteRspCallback extends Callback {void invoke(Pointer userData, String instrumentId, String errorMsg, int errorId, boolean bIsLast);}// 深度行情推送public interface OnRtnDepthMarketDataCallback extends Callback {void invoke(Pointer userData, String instrumentId, double lastPrice, int volume,double askPrice1, int askVolume1, double bidPrice1, int bidVolume1, double settlementPrice,double upperLimitPrice,double lowerLimitPrice,String tradingDay);}// 订阅询价响应public interface OnRtnForQuoteRspCallback extends Callback {void invoke(Pointer userData, String instrumentId, String forQuoteSysId);}// 结构体字段(与C头文件对应)public OnFrontConnectedCallback OnFrontConnected;public OnFrontDisconnectedCallback OnFrontDisconnected;public OnHeartBeatWarningCallback OnHeartBeatWarning;public OnRspUserLoginCallback OnRspUserLogin;public OnRspUserLogoutCallback OnRspUserLogout;public OnRspSubMarketDataCallback OnRspSubMarketData;public OnRspUnSubMarketDataCallback OnRspUnSubMarketData;public OnRspSubForQuoteRspCallback OnRspSubForQuoteRsp;public OnRspUnSubForQuoteRspCallback OnRspUnSubForQuoteRsp;public OnRtnDepthMarketDataCallback OnRtnDepthMarketData;public OnRtnForQuoteRspCallback OnRtnForQuoteRsp;public Pointer userData;// 字段顺序(必须与C结构体一致)@Overrideprotected List<String> getFieldOrder() {return Arrays.asList("OnFrontConnected","OnFrontDisconnected","OnHeartBeatWarning","OnRspUserLogin","OnRspUserLogout","OnRspSubMarketData","OnRspUnSubMarketData","OnRspSubForQuoteRsp","OnRspUnSubForQuoteRsp","OnRtnDepthMarketData","OnRtnForQuoteRsp","userData");}
}

5、初始化并运行

  1. 创建行情API实例
  2. 注册回调
  3. 注册前置机地址
  4. 登录
  5. 订阅行情
  6. 买入报单录入:定时发送
  7. 行情推送:回调
  8. 登出

import com.sun.jna.Pointer;
/***  期货行情订阅接口**  1. 创建行情API实例*  2. 注册回调*  3. 注册前置机地址*  4. 登录*  5. 订阅行情*  6. 买入报单录入:定时发送*  7. 行情推送:回调*  8. 登出**/
public class CTPMdDemo {// 注意:实际使用时需要替换为官方提供的服务器地址、端口和认证信息static String frontAddress = "tcp://180.166.xx.xx:51218";static String brokerid = "xxxx";static String username = "xxxx";static String password = "xxxx";public static void main(String[] args) {// 1. 创建回调结构体CTPMdCallbacks callbacks = new CTPMdCallbacks();initCallbacks(callbacks);Pointer spi = CTPMdApi.INSTANCE.CreateMdSpi(callbacks);// 2. 创建行情API实例(使用默认参数)Pointer api = CTPMdApi.INSTANCE.CreateMdApi("", false, false);// 3. 注册回调CTPMdApi.INSTANCE.MdApi_RegisterSpi(api, spi);// 4. 注册前置机地址int rst = CTPMdApi.INSTANCE.MdApi_RegisterFront(api, frontAddress);System.out.println("注册前置机地址:" + rst);// 5. 初始化APICTPMdApi.INSTANCE.MdApi_Init(api);System.out.println("初始化API成功");// 阻塞主线程,等待链接成功回调通知LockUtil.waitConnect();// 6. 登录(实际使用时替换为真实信息)String loginResult = CTPMdApi.INSTANCE.MdApi_ReqUserLogin(api, brokerid, username, password, 2);System.out.println("登录请求结果:" + loginResult);// 登录成功后查询行情LockUtil.waitLogin();// 7. 订阅行情(示例合约)String[] instruments = {"eb2510"};CTPMdApi.INSTANCE.MdApi_SubscribeMarketData(api, instruments, instruments.length);// 阻塞主线程(实际应用中需合理处理)try {Thread.sleep(600000); // 运行1分钟} catch (InterruptedException e) {e.printStackTrace();}// 8. 登出int logout = CTPMdApi.INSTANCE.MdApi_ReqUserLogout(api, brokerid, username, 2);// 9. 释放资源CTPMdApi.INSTANCE.MdApi_Release(api);CTPMdApi.INSTANCE.ReleaseMdSpi(spi);System.out.println("释放资源成功");}// 初始化回调函数private static void initCallbacks(CTPMdCallbacks callbacks) {callbacks.userData = Pointer.NULL; // 可传递Java对象指针// 前置机连接成功回调callbacks.OnFrontConnected = (userData) -> {// 发送信号通知主线程System.out.println("前置机链接成功");LockUtil.connected();};// 登录响应回调callbacks.OnRspUserLogin = (userData, errorMsg, errorId) -> {if (errorId == 0) {LockUtil.logined();System.out.println("登录成功:" + userData);} else {System.out.println("~~~登录失败:" + errorMsg + "(错误码:" + errorId + ")");}};callbacks.OnRspUserLogout = (userData, errorMsg, errorId) -> {System.out.println("登出:" + errorMsg + "(错误码:" + errorId + ")");};callbacks.OnRspSubMarketData = (userData, instrumentId, errorMsg, errorId, bIsLast) -> {System.out.println("订阅行情响应:" + instrumentId + " " + errorMsg + "(错误码:" + errorId + ")");};// 行情推送回调callbacks.OnRtnDepthMarketData = (userData, instrumentId, lastPrice, volume,askPrice1, askVolume1, bidPrice1, bidVolume1, settlementPrice,upperLimitPrice,lowerLimitPrice,tradingDay) -> {// 打印所有参数System.out.printf("行情推送 - 合约:%s,最新价:%s,成交量:%s,卖一:%s(%d),买一:%s(%d),结算价:%s,涨停价:%s,跌停价:%s,交易日:%s\n",instrumentId, lastPrice, volume, askPrice1, askVolume1, bidPrice1, bidVolume1, settlementPrice,upperLimitPrice,lowerLimitPrice,tradingDay);};callbacks.OnRspSubForQuoteRsp = (userData, instrumentId, errorMsg, errorId, bIsLast) -> {System.out.println("订阅询价响应:" + instrumentId + " " + errorMsg + "(错误码:" + errorId + ")");};// 其他回调按需实现...}
}

LockUtil.java 工具类

public class LockUtil {private static final Object lockConnect = new Object();private static final Object lockLogin = new Object();private static final Object lockAuth = new Object();public static void waitConnect() {synchronized (lockConnect) {try {lockConnect.wait(); //} catch (InterruptedException e) {e.printStackTrace();}}}public static void connected() {synchronized (lockConnect) {lockConnect.notifyAll();}}public static void waitLogin() {synchronized (lockLogin) {try {lockLogin.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public static void logined() {synchronized (lockLogin) {lockLogin.notifyAll();}}
}

注意事项

  1. 平台依赖:.dll 是 Windows 特有,Linux 对应 .so,macOS 对应 .dylib,跨平台需分别编译。
  2. 函数名和参数匹配:Java 映射的函数名和参数类型必须与 DLL 中的函数严格一致,否则会报 UnsatisfiedLinkError
  3. 数据类型映射:C++ 与 Java 的数据类型需严格对应(如char*→String,double→double),避免内存错误。
    • Java 中的 int 对应 C 中的 jint
    • Java 中的 String 需通过 JNI 函数(如GetStringUTFChars)转换为 C 字符串
  4. 线程安全:CTP 回调在独立线程中执行,Java 层需处理线程同步(如使用 synchronized)
  5. 动态库加载:确保 Java 能找到适配层的.dll/.so(通过-Djava.library.path指定路径)。
  6. DLL 依赖:若 .dll 依赖其他 DLL,需确保这些依赖也能被找到。
http://www.dtcms.com/a/398832.html

相关文章:

  • 新网 主办网站已备案建站系统做网站
  • 网站备案 历史seo流量排行榜神器
  • C++ 中的 static 关键字:类成员、局部变量与单例模式
  • 【 设计模式 | 行为型模式 观察者模式 】
  • seo 网站案例怀化优化网站排名
  • Rust 最小可行 MQ 架构实现指南
  • 公司网站设计报价电商网站建设设计报告总结
  • 【Python】迭代器
  • 【数据迁移】:MySQL 环境下【大表定义变更】一致性保障与数据迁移优化方案
  • 织梦禁止网站右击重庆企业
  • 金融系统的“防火墙”:数字孪生如何模拟风险攻击
  • 埃拉托斯特尼筛法(Sieve of Eratosthenes)——原理、复杂度与多种 C++ 实现
  • 【大模型-金融】Trading-R1 多阶段课程学习
  • 建网站知乎怎么样上传网站资料
  • jupyter notebook 使用集锦(持续更新)
  • 部署开源PPTagent 生成工具
  • Python的大杀器:Jupyter Notebook处理.ipynb文件
  • 物流网站建设与管理规划书七牛wordpress插件
  • 【同源策略】跨域问题解决方法(多种)
  • 【数据结构】链表 --- 单链表
  • ArcGIS JSAPI 高级教程 - 自由绘制线段、多边形
  • 【2025最新】ArcGIS 点聚合功能实现全教程(进阶版)
  • Express使用教程(二)
  • 大模型部署基础设施搭建 - Docker
  • 芜湖建设机械网站企业管理系统软件下载
  • 永嘉县住房和城乡规划建设局网站自助贸易网
  • 华为云学习笔记(1):ECS 实例操作与密钥登录实践
  • 有一次django开发实录
  • RISC-V 中的 Wait For Interrupt 指令 (wfi) 详解
  • 前端核心框架vue之(指令案例篇1/5)