在openfire的官网上,有支持Java语言的asmack,也有支持C#语言的XMPP,但是有些应用场景可能需要C++语言作为openfire的客户端。本篇文章主要介绍如何利用asmack来实现C++语言编写的客户端。(需要有一定的jni基础)
第一步:利用Java语言和asmack可以完美的和openfire进行通讯(消息的收发),那么可以将Java平台下的该工程打包成jar包;
用eclipse打包时,由于项目里面含有多个第三方jar包,需要注意的是在src平级目录下新建MANIFEST.MF文件,文件内容如下:
Manifest-Version: 1.0
Class-Path: lib/asmack-jse-buddycloud-2010.12.11.jar lib/kxml2-2.2.2.jar lib/xmlpull_1_0_5.jar
Main-Class: com.uuorange.example.MainScene
注意:冒号后面都有空格,两个jar包之间也有空格,最后需要空一行,打包过程不在详细介绍
打包的jar包和放在lib目录下引入的第三方jar包,在使用时,需要放在同一目录下
第二步:C++调用Java的jar包以及jar包里面的函数;
在win32的Visual studio 2008上进行环境配置和编码实现(反键项目 属性->配置属性->C/C++->常规->附加包含目录:
"...../java/jdk/include/win32","...../java/jdk/include")
关键代码:
#include "stdafx.h"
#include "jni.h"
#include <Windows.h>
bool startJVM();
int _tmain(int argc, _TCHAR* argv[])
{
startJVM();
return 0;
}
typedef jint(JNICALL *JNICREATEPROC)(JavaVM **, void **, void *);
//设置输出流
bool setStream(JNIEnv *env, const char* fileName, const char* method);
//启动java虚拟机
bool startJVM(){
//获取jvm动态库的路径
TCHAR* jvmPath = _T("C://Program Files//Java//jdk1.8.0_65//jre//bin//client//jvm.dll");
//java虚拟机启动时接收的参数,每个参数单独一项
int nOptionCount = 2;
JavaVMOption vmOption[2];
//设置JVM最大允许分配的堆内存,按需分配
vmOption[0].optionString = "-Xmx256M";
//设置classpath
vmOption[1].optionString = "-Djava.class.path=e:/FunCall_C2Java_Callee.jar";
JavaVMInitArgs vmInitArgs;
vmInitArgs.version = JNI_VERSION_1_6;
vmInitArgs.options = vmOption;
vmInitArgs.nOptions = nOptionCount;
//忽略无法识别jvm的情况
vmInitArgs.ignoreUnrecognized = JNI_TRUE;
//设置启动类,注意分隔符为"/"
const char startClass[] = "com/uuorange/example/MainScene";
//启动方法,一般是main函数,当然可以设置成其他函数
const char startMethod[] = "main";
//加载JVM,注意需要传入的字符串为LPCWSTR,指向一个常量Unicode字符串的32位指针,相当于const wchar_t*
HINSTANCE jvmDLL = LoadLibrary(jvmPath);
if(jvmDLL == NULL){
printf("加载JVM动态库错误");
return false;
}
//初始化jvm物理地址
JNICREATEPROC jvmProcAddress = (JNICREATEPROC)GetProcAddress(jvmDLL, "JNI_CreateJavaVM");
if(jvmDLL == NULL){
FreeLibrary(jvmDLL);
printf("加载JVM动态库错误");
return false;
}
//创建JVM
JNIEnv *env;
JavaVM *jvm;
jint jvmProc = (jvmProcAddress)(&jvm, (void **)&env, &vmInitArgs);
if(jvmProc < 0 || jvm == NULL ||env == NULL){
FreeLibrary(jvmDLL);
printf("创建JVM错误");
return false;
}else{
printf("创建JVM成功\n");
}
//加载启动类
jclass mainclass = env ->FindClass(startClass);
if(env -> ExceptionCheck() == JNI_TRUE || mainclass == NULL){
env -> ExceptionDescribe();
env -> ExceptionClear();
FreeLibrary(jvmDLL);
printf("加载启动类错误");
return false;
}else{
printf("加载启动类成功\n");
}
//jmethodID mid = env->GetStaticMethodID(mainclass, "fun", "()V");
//env->CallStaticVoidMethod(mainclass, mid, 100);
jobject obj = env->AllocObject(mainclass);
//jmethodID mid1 = env->GetMethodID(mainclass, "call", "()Ljava/lang/String;");//Ljava/lang/String;Ljava/lang/String;I
jmethodID mid1 = env->GetMethodID(mainclass, "call", "()V");
jstring msg = (jstring)env->CallObjectMethod(obj, mid1);
jstring to = env->NewStringUTF("test2");
jstring content = env->NewStringUTF("hello, I am from C++");
printf("开始睡觉\n");
Sleep(10000);
printf("继续执行\n");
jmethodID mid2 = env->GetStaticMethodID(mainclass, "sendMessage", "(Ljava/lang/String;Ljava/lang/String;I)V");
env->CallStaticVoidMethod(mainclass, mid2, to, content, 1);
printf("加载启动类成功\n");
//jvm释放
jvm -> DestroyJavaVM();
return true;
}
第三步:通过上面两步可以实现启动jvm、启动jar包、通过openfire给指定的客户端发送消息。C++客户端在接收消息时,只能在Java jar包里面接收到消息,没法通过回调传给上层的C++,为了达到Java jar包里面的数据能够传给C++,好像没有特别好的方法,如果某个大神有好方法,希望告知,谢谢了。本人为了实现该功能,在C++客户端上的C++层和Java层建立了一个TCP的长连接,这样Java收到的即时消息可以通过TCP传给上层的C++。
PS:最近在XMPP的官网上(https://xmpp.org/software/)发现了XMPP支持的各种语言的Clients、Servers和Libraries,包括对应的API文档都有(英语阅读能力要好)。