WCF over JMS 与 gSoap over JMS 通信总结 一共有三个项目: 项目1:WCFServiceLibrary 定义接口 项目2:C++的Server 项目3:C#的Client
步骤一:定义接口(项目1) 把项目2调用项目3的接口和项目3调用项目2的接口都集成到项目1里,接口可以为Request/Response模式和OneWay模式,接口实现为空,BasicHttpBinding
步骤二:项目2使用接口 1、在某个文件夹下放入gSoap包内的三个文件wsdl2h.exe,soapcpp2.exe,typemap.dat和三个文件夹import,extras,custom 2、运行项目1,获取wsdl地址,用gSoap提供的工具wsdl2h.exe生成接口头文件gSoap.h cmd: wsdl2h.exe -o gSoap.h http://localhost:6666/Interface/Service/?wsdl 3、用gSoap提供的工具soapcpp2.exe生成C++需要的代理文件 cmd: soapcpp2.exe -1 -i -x -a -IimportgSoap.h -1 代表使用SOAP1.1协议,-i代表生成C++代理类,-x代表不生成xml文件,-a代表加入action检查,-Iimport代表使用import文件夹内的文件,其中import中的文件又会牵扯到另两个文件夹 4、删除gSoap.h文件。 第2到4步可以用一个bat文件代替,bat文件存放在当前文件夹,内容为: echo off wsdl2h.exe -o gSoap.hhttp://localhost:6666/Interface/Service/?wsdl soapcpp2.exe -1 -i -x -a -Iimport gSoap.h del gSoap.h Pause 5、双击bat文件生成soapH.h、soapC.cpp、soapStub.h、-.nsmap、-Proxy.h、-Proxy.cpp、-Service.h、-Service.cpp共八个文件。把这八个文件放到项目2的一个gSoap文件夹下,并把gSoap包提供的stdsoap2.h、stdsoap2.cpp、duration.h、duration.cpp也放到此文件夹内 6、修改stdsoap2.cpp文件,添加宏(若不添加此宏,链接时会出错,算是gSoap的bug): #ifndef WITH_NONAMESPACES #define WITH_NONAMESPACES #endif 7、修改duration.cpp文件,此文件中只有xsd系列的5个函数,仿照着写出ns3系列的函数。这应该是gSoap的一个bug,如果不实现这五个函数,链接会出错 8、修改-Proxy.h、-Proxy.cpp文件。删除本项目作为服务端的代码 9、修改-Service.h、-Service.cpp文件。删除本项目作为客户端的代码。实现其他接口 10、项目2引入此文件夹及这12个文件,即可实现Http通信功能。 11、实现over JMS功能。加入JMS封装类QueueProducer、QueueConsumer、TopicProducer。在-Proxy.cpp、-Service.cpp文件中,把通信底层换成JMS。 Service端函数: static int serve___ns1__Fun(BasicHttpBinding_USCOREIServiceService*soap) { struct__ns1__Fun soap_tmp___ns1__Fun; _ns1__FunResponsens1__FunResponse; ns1__FunResponse.soap_default(soap); soap_default___ns1__Fun(soap,&soap_tmp___ns1__Fun); soap->encodingStyle= NULL; if(!soap_get___ns1__Fun(soap, &soap_tmp___ns1__Fun, "-ns1:Fun",NULL)) return soap->error; if(soap_body_end_in(soap) || soap_envelope_end_in(soap) || soap_end_recv(soap)) {/*returnsoap->error;*/} soap->error= soap->Fun(soap_tmp___ns1__Fun.ns1__Fun, &ns1__FunResponse);
soap->action= "http:///IService/FunResponse";
soap->mode= 1; soap->bufidx= 0; soap->ns= 0; memset(soap->buf,0, 65536);
if(soap_envelope_begin_out(soap) || soap_putheader(soap) || soap_body_begin_out(soap) || ns1__FunResponse.soap_put(soap,"ns1:FunResponse", "") || soap_body_end_out(soap) || soap_envelope_end_out(soap)) return soap->error;
soap->buflen= soap->bufidx; return0; }
Proxy端函数: int BasicHttpBinding_USCOREIServiceProxy::Fun(constchar *endpoint, const char *soap_action, _ns1__Fun *ns1__Fun, _ns1__FunResponse*ns1__FunResponse) { structsoap *soap = this; struct__ns1__Fun soap_tmp___ns1__Fun; soap_tmp___ns1__Fun.ns1__Fun= ns1__Fun;
soap->action= "http:///IService/Fun";
memset(soap->buf,0, 65536); soap->mode= 1; // 消息添加进buf中而不是显示在屏幕上 soap->ns= 0; // 添加头 soap->bufidx= 0;
if(soap_envelope_begin_out(soap) || soap_putheader(soap) || soap_body_begin_out(soap) || soap_put___ns1__Fun(soap,&soap_tmp___ns1__Fun, "-ns1:Fun", NULL) || soap_body_end_out(soap) || soap_envelope_end_out(soap)) return soap->error;
// 发送消息并接收响应 stringqstrSendMSG = string(soap->buf, soap->bufidx); stringqstrReturnMSG; m_pQueueProducer->SendQueueMessage(qstrSendMSG,qstrReturnMSG);
if(qstrReturnMSG.empty()) { return 0; } intnLength = qstrReturnMSG.length();
strncpy(soap->buf,qstrReturnMSG.c_str(), nLength); soap->bufidx= 0; soap->buflen= nLength; soap->mode= 0; soap->peeked= 0; soap->ahead= 0;
if(soap_envelope_begin_in(soap) || soap_recv_header(soap) || soap_body_begin_in(soap)) return soap->error; ns1__FunResponse->soap_get(soap,"ns1:FunResponse", "");
return0; }
12、-Service类中加入一个函数disposeBuf,当接收到JMS消息时,把消息内容复制到soap->buf中,然后进行dispatch(),决定到底使用哪个接口
void QueueConsumer::onMessage( const Message* message) throw() { try { const TextMessage* textMessage = dynamic_cast<const TextMessage* >( message );
std::string text = "";
if( textMessage != NULL ) { text= textMessage->getText().c_str(); if(text.empty()) return; }
m_pService->disposeBuf(text); std::string qstrReplyMSG =std::string(m_pService->buf, m_pService->buflen); ...
//d add { voidBasicHttpBinding_USCOREIServiceService::disposeBuf(std::string strMSG) { intnLength = strlen(strMSG.c_str()); memset(this->buf,0, 65536); strncpy(this->buf,strMSG.c_str(), nLength); this->buflen= nLength; this->bufidx= 0;
soap_envelope_begin_in(this); soap_recv_header(this);// 删除此句没影响 soap_body_begin_in(this);
dispatch(); } //d add }
13、修改soapC.cpp,在soap_out_SOAP_ENV__Header()、soap_in_SOAP_ENV__Header()两个函数中加入处理action的代码
SOAP_FMAC3 int SOAP_FMAC4soap_out_SOAP_ENV__Header(struct soap *soap, const char *tag, int id, conststruct SOAP_ENV__Header *a, const char *type) { (void)soap;(void)tag; (void)id; (void)type; if(soap_element_begin_out(soap, tag, soap_embedded_id(soap, id, a,SOAP_TYPE_SOAP_ENV__Header), type)) return soap->error; //d add{ if(soap_element_begin_out(soap, "Actionxmlns=\"http://schemas.microsoft.com/ws/2005/05/addressing/none\"",id, type) || soap_string_out(soap,soap->action, 0) || soap_element_end_out(soap,"Action")) return soap->error; //d add} returnsoap_element_end_out(soap, tag); }
SOAP_FMAC3 struct SOAP_ENV__Header * SOAP_FMAC4 soap_in_SOAP_ENV__Header(structsoap *soap, const char *tag, struct SOAP_ENV__Header *a, const char *type) { if(soap_element_begin_in(soap, tag, 0, type)) return NULL;
//d add{ soap_in_string(soap,"Action", &soap->action, "xsd:string"); //d add} ...
14、在使用客户端时,让soap->header不为空,则可以加上header和action
m_pProxy = newBasicHttpBinding_USCOREIServiceProxy(SOAP_C_UTFSTRING); m_pProxy->header = new struct SOAP_ENV__Header();
步骤三:项目3使用接口 1、修改项目1,使项目1仅具有项目3作为客户端时的接口 2、项目3引用项目1的服务,此时,项目3具备了作为客户端的功能 3、修改项目1,使项目1仅具有项目3作为服务端时的接口 4、项目3获得项目1的IService.cs,Service.cs两个文件,并把项目1的app.config文件合并进项目3,此时,项目3具备了作为服务端的功能 5、实现over JMS,略。
步骤四:接口规则 1、项目1定义接口时,参数可以使用C#普通类型(int、char、double、float、string)、结构体、List 2、C#端定义string,可以用string str = ""; 不要用string str = null; 3、String类型赋值时要Base64 encode,得到时要Base64 decode 4、C++端接口如果是Request/Response模式,则有两个结构体参数,第一个是输入参数,第二个是输出参数;如果是OneWay模式,则有一个结构体参数。 5、C++端-Service类接口实现时,由于结构体参数内部大部分都为指针,而且如果用对象给输出参数赋值的话,经常会出现对象销毁而打包失败,故采取以下措施: intBasicHttpBinding_USCOREIService1Service::ClientLogin(_ns1__ClientLogin *ns1__ClientLogin,_ns1__ClientLoginResponse *ns1__ClientLoginResponse) { // 防止多次泄露,这样只泄露一次的内存空间 static_ns1__ClientLoginResponse* pClientLoginResponse = NULL; if(pClientLoginResponse != NULL) { deletepClientLoginResponse->ClientLoginResult; pClientLoginResponse->ClientLoginResult = NULL; deletepClientLoginResponse->error; pClientLoginResponse->error = NULL; deletepClientLoginResponse->sessionId; pClientLoginResponse->sessionId = NULL;
delete pClientLoginResponse; pClientLoginResponse = NULL; }
pClientLoginResponse = new _ns1__ClientLoginResponse(); pClientLoginResponse->ClientLoginResult = new bool; pClientLoginResponse->error = new string; pClientLoginResponse->sessionId = new string; // 防止多次泄露处理完毕 ... *ns1__ClientLoginResponse =*pClientLoginResponse;
赋值时多采用【*指针 = *指针】的方式
|
|