ctp java_期货 CTP的JAVA接口 JNI实现

用JNI直接实现CTP API

这里记录一下思路,做个纪念。防止以后忘记了~~

参考了SWIG的一些做法(就是照抄了基本思路),例如director类(SPI往回调用),比如methodID的数组。

但是也有很多不一样的地方(偷懒的),例如C++的SPI的OnXXX函数回调Java方法时,考虑子类继承时,SWIG会做很多判断,本文忽略了很多细节。

还例如SWIG所有的CTP数据结构都使用C++原生的,然后java中通过native的setter和getter来操作,本文的数据结构都是纯java对象。

编译连接生成了Windows和linux(Ubuntu)的64位的接口,放在了一个jar中

CTP的头文件分为3个部分:

1. ThostFtdcUserApiDataType.h,api的数据类型, typedef了一大堆类型,char, char[], int, short, double。 对于大部分char型,又#define了很多字符常量。开头的枚举类型比较特殊。还有那个单引号括起来的连续字符也比较特殊。

2. ThostFtdcUserApiStruct.h, api的数据结构,用于api和spi的函数参数。数据结构的成员变量的类型全部来自上面的头文件

3.  API、SPI (Trader, Market Data)

思路:

Java的API类跟C++中一样,只能通过静态方法例如CreateFtdcTraderApi方法来创建。

Java的API类,包含一个C++的API的实例的指针。API的每个函数,全部通过native方法。这个native方法通过这个指针调用对应的CTP API。例如:

public native int ReqUserLogin(CThostFtdcReqUserLoginField pReqUserLoginField,int nRequestID);

Java中的SPI类可以用new来创建,它的构造函数会调用newNativeSpiInstance这个native方法。newNativeSpiInstance(代码最长的函数)会干这么几件事:

获得Java的SPI实例的全局引用;

获得java线程的JVM指针(因为JNIEnv指针只有当前线程有效,SPI的OnXXX是新线程,这个指针会失效)

以Java的SPI实例的全局引用和JVM指针作为参数,创建一个C++中的SPI子类

Java中的SPI类中的OnXXX函数,则是通过CTP的SPI实例的OnXXX函数直接调用。例如:

voidOnFrontConnected()

{

JWrap jw(this);

JNIEnv* env =jw.getEnv();

env->CallVoidMethod(jspi,spi_methodIDs[0]);

}

开搞:

1. 为每个ctp api的数据结构创建对应的java类

char[]在java中可以使用String表示。注意ctp中是gb2312编码,这里可以使用下面的String函数直接转换(没测试)

packagectp.apistruct;public classCThostFtdcBrokerSyncField {public String BrokerID = ""; //char[11] (TThostFtdcBrokerIDType)

publicCThostFtdcBrokerSyncField(){}public CThostFtdcBrokerSyncField(byte[] BrokerID){try{ this.BrokerID= new String(BrokerID, "gb2312"); }catch(java.io.UnsupportedEncodingException e){}

}

}

2. 实现每一个java API类的native函数

非原生类型的变量需要调用java的构造函数来创建java对象

JNIEXPORT jint JNICALL Java_ctp_CThostFtdcTraderApi_ReqQryTradingNotice

(JNIEnv*env,jobject obj,jobject pQryTradingNotice,jint nRequestID)

{

CThostFtdcTraderApi*ptrApi;

jclass clazzTraderApi= env->FindClass("Lctp/CThostFtdcTraderApi;");

jfieldID fidTraderApi= env->GetFieldID(clazzTraderApi, "ptrApi", "J");

ptrApi= (CThostFtdcTraderApi*)env->GetLongField(obj,fidTraderApi);

jclass clzparam= env->FindClass("Lctp/apistruct/CThostFtdcQryTradingNoticeField;");

CThostFtdcQryTradingNoticeField QryTradingNotice= { 0};

{

jfieldID fid= env->GetFieldID(clzparam, "BrokerID", "Ljava/lang/String;");

jstring jstr= (jstring) env->GetObjectField(pQryTradingNotice,fid);const char*cstr;if(jstr) {

cstr= env->GetStringUTFChars(jstr, NULL);

strcpy(QryTradingNotice.BrokerID, (char *) cstr);

}

env->ReleaseStringUTFChars((jstring)jstr, cstr);

}

{

jfieldID fid= env->GetFieldID(clzparam, "InvestorID", "Ljava/lang/String;");

jstring jstr= (jstring) env->GetObjectField(pQryTradingNotice,fid);const char*cstr;if(jstr) {

cstr= env->GetStringUTFChars(jstr, NULL);

strcpy(QryTradingNotice.InvestorID, (char *) cstr);

}

env->ReleaseStringUTFChars((jstring)jstr, cstr);

}

{

jfieldID fid= env->GetFieldID(clzparam, "InvestUnitID", "Ljava/lang/String;");

jstring jstr= (jstring) env->GetObjectField(pQryTradingNotice,fid);const char*cstr;if(jstr) {

cstr= env->GetStringUTFChars(jstr, NULL);

strcpy(QryTradingNotice.InvestUnitID, (char *) cstr);

}

env->ReleaseStringUTFChars((jstring)jstr, cstr);

}

jint iRtn= ptrApi->ReqQryTradingNotice(&QryTradingNotice, ( int) nRequestID);returniRtn;

}

3. 在C++中实现一个SPI的实例,让它的OnXXX函数去调用对应Java SPI类中的OnXXX函数

主要任务是创建java对象,作为参数去调用对应的Java SPI方法。

void OnRspUserLogin(CThostFtdcRspUserLoginField * pRspUserLogin, CThostFtdcRspInfoField * pRspInfo, int nRequestID, boolbIsLast)

{

JWrap jw(this);

JNIEnv* env =jw.getEnv();

jobject RspUserLogin;if(pRspUserLogin)

{

jbyteArray TradingDay= env->NewByteArray(9);if(pRspUserLogin->TradingDay)

env->SetByteArrayRegion(TradingDay , 0, 9, (const jbyte*)pRspUserLogin->TradingDay);

jbyteArray LoginTime= env->NewByteArray(9);if(pRspUserLogin->LoginTime)

env->SetByteArrayRegion(LoginTime , 0, 9, (const jbyte*)pRspUserLogin->LoginTime);

jbyteArray BrokerID= env->NewByteArray(11);if(pRspUserLogin->BrokerID)

env->SetByteArrayRegion(BrokerID , 0, 11, (const jbyte*)pRspUserLogin->BrokerID);

jbyteArray UserID= env->NewByteArray(16);if(pRspUserLogin->UserID)

env->SetByteArrayRegion(UserID , 0, 16, (const jbyte*)pRspUserLogin->UserID);

jbyteArray SystemName= env->NewByteArray(41);if(pRspUserLogin->SystemName)

env->SetByteArrayRegion(SystemName , 0, 41, (const jbyte*)pRspUserLogin->SystemName);

jbyteArray MaxOrderRef= env->NewByteArray(13);if(pRspUserLogin->MaxOrderRef)

env->SetByteArrayRegion(MaxOrderRef , 0, 13, (const jbyte*)pRspUserLogin->MaxOrderRef);

jbyteArray SHFETime= env->NewByteArray(9);if(pRspUserLogin->SHFETime)

env->SetByteArrayRegion(SHFETime , 0, 9, (const jbyte*)pRspUserLogin->SHFETime);

jbyteArray DCETime= env->NewByteArray(9);if(pRspUserLogin->DCETime)

env->SetByteArrayRegion(DCETime , 0, 9, (const jbyte*)pRspUserLogin->DCETime);

jbyteArray CZCETime= env->NewByteArray(9);if(pRspUserLogin->CZCETime)

env->SetByteArrayRegion(CZCETime , 0, 9, (const jbyte*)pRspUserLogin->CZCETime);

jbyteArray FFEXTime= env->NewByteArray(9);if(pRspUserLogin->FFEXTime)

env->SetByteArrayRegion(FFEXTime , 0, 9, (const jbyte*)pRspUserLogin->FFEXTime);

jbyteArray INETime= env->NewByteArray(9);if(pRspUserLogin->INETime)

env->SetByteArrayRegion(INETime , 0, 9, (const jbyte*)pRspUserLogin->INETime);

jclass jclazz= env->FindClass("Lctp/apistruct/CThostFtdcRspUserLoginField;");

RspUserLogin= env->NewObject(jclazz, ctp_struct_methodIDs[2],TradingDay,LoginTime,BrokerID,UserID,SystemName,pRspUserLogin->FrontID,pRspUserLogin->SessionID,MaxOrderRef,SHFETime,DCETime,CZCETime,FFEXTime,INETime);

}

jobject RspInfo;if(pRspInfo)

{

jbyteArray ErrorMsg= env->NewByteArray(81);if(pRspInfo->ErrorMsg)

env->SetByteArrayRegion(ErrorMsg , 0, 81, (const jbyte*)pRspInfo->ErrorMsg);

jclass jclazz= env->FindClass("Lctp/apistruct/CThostFtdcRspInfoField;");

RspInfo= env->NewObject(jclazz, ctp_struct_methodIDs[18],pRspInfo->ErrorID,ErrorMsg);

}

env->CallVoidMethod(jspi,spi_methodIDs[4],RspUserLogin, RspInfo, nRequestID, bIsLast);

}

坑与一些细节

出现segment fault然后导致Java结束,主要原因是查找类的时候,类的名字需要是全名。名字找对了,并且空指针都处理了,基本上就不会出现了。


版权声明:本文为weixin_42310572原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。