提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
项目与第三方系统进行可选的Http/WebServer通信,接口协议文档修改,由原先项目仅作为客户端向第三方系统发送请求,现新增加一个接口,需项目作为服务端接收第三方系统发送的请求并做出应答。
结合项目之前的实现方法:Http传输协议使用libcurl库做客户端、WebServer传输协议使用gsoap生成客户端
确定项目目前服务端的实现方法:Http使用libevent库做服务端、WebServer使用gsoap生成服务端
一、libevent实现Http简易服务器
1.libevent安装
具体安装方法优快云上有很多教程可自行寻找,本人是按照这个教程进行的windows下编译及使用libevent,但我当时按这个教程做,在*(4)使用VS命令提示工具编译:*这一步出现了几个小问题导致编译失败,后来查找解决办法才编译成功。
附官网下载链接:http://libevent.org/
2.实现Http简易服务器
首先在项目中新建一个HttpServer类,你可以在这个类的构造函数中直接开启服务器,也可以写一个StartServer接口在里面实现
在开始之前,HttpServer.cpp文件要加上以下头文件(最好全部加上,可能有一些对你来说是多余的)
#include <sys/types.h>
#include <event2/event-config.h>
#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <winsock2.h>
#endif
#include <time.h>
#ifdef _EVENT_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/util.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <evhttp.h>
#include <signal.h>
#include <ctype.h>
#pragma comment (lib,"Iphlpapi.lib")
#include <assert.h>
#include <corecrt_io.h>
这里服务器使用libevent库内置的evhttp服务器,代码如下
//这个函数功能是处理接收到的请求并产生应答
void now_handler(struct evhttp_request *req, void *arg)
{
struct evbuffer *buf;
buf = evbuffer_new();
//获取客户端请求的URI(使用evhttp_request_uri或直接req->uri)
// 分析请求
char *decode_uri = _strdup((char*)evhttp_request_uri(req));
//获取POST方法的数据 也有获取GET数据的方法只是这里没用到
char *post_data = (char *)EVBUFFER_DATA(req->input_buffer);
char *result;
//在这里通过消息机制将接收到的请求传出并阻塞,等待信息处理完成后往下走,如果处理逻辑简单可以直接在这里处理
::SendMessage((HWND)hWndParent, nMsgType, (WPARAM)post_data, (LPARAM)&result);
// 返回HTTP头部
evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=UTF-8");
evhttp_add_header(req->output_headers, "Server", "my_httpd");
evhttp_add_header(req->output_headers, "Connection", "close");
//这里给应答赋值的是消息处理结束之后对应产生的应答 有一个接口设置strReply,在消息接收方处理消息完成后调用
result = const_cast<char*>(strReply.c_str());
// 将要输出的值写入输出缓存
evbuffer_add_printf(buf, "%s", result);
// 输出
evhttp_send_reply(req, HTTP_OK, "OK", buf);
// 内存释放
evbuffer_free(buf);
}
//开启服务器的接口 建议在项目中单独开启一个std::thread运行
//比如:m_thread = std::thread(&HttpServer::StartServer, this);
void HttpServer::StartServer()
{
//使用libevent内置的evhttp服务器
struct evhttp *httpd;
event_init();
//这里设置服务器的IP以及端口,如127.0.0.1 8080
httpd = evhttp_start(m_strIpAddress.c_str(),m_nPort);
/* Set a callback for requests to "/specific". */
/* evhttp_set_cb(httpd, "/specific", another_handler, NULL); */
/* Set a callback for all other requests. */
//这里设置服务器接收到请求之后处理请求的函数
evhttp_set_gencb(httpd, now_handler, NULL);
//服务器挂起
event_dispatch();
/* Not reached in this code as it is now. */
evhttp_free(httpd);
}
到这里你创建的服务器就开始运行了,接下来你可以在浏览器上输入网址例如http://127.0.0.1:8080/,就可以正常访问了。
二、gsoap实现WebServer简易服务器
1.准备工作
自己看的一个教程Gsoap使用精华总结
下载goap后解压,下载地址 https://sourceforge.net/projects/gsoap2/files/
在你的项目目录中新建一个WebServer的文件夹,在你解压后的D:\gsoap_2.8.122\gsoap-2.8\gsoap\bin目录下拷贝soapcpp2.exe和wsdl2h.exe文件到WebServer文件夹(建议再在D:\gsoap_2.8.122\gsoap-2.8\gsoap拷贝一个typemap.dat文件过去,否则后面会提示缺少这个文件,虽然貌似没有影响),再准备一个描述接口的.wsdl文件或者.h文件就可以生成服务器了,这里的例子可以看上面的教程。
2.实现WebServer简易服务器
打开cmd,cd到WebServer文件夹下
输入:Wsdl2h.exe -s xxx.wsdl (此时会生成xxx.h文件)
再输入:Soapcpp2 -S -i xxx.h(Soapcpp2 详细命令可以参考教程)
然后把生成的soapC.cpp、soapH.h、soapStub.h、stdsoap2.h、stdsoap2.cpp、soapxxxService.h、soapxxxService.cpp等文件加入到项目中,然后在soapxxxService.cpp实现增加的接口,例如
//函数功能根据请求应答
int xxxService::pushInfo(char * infoJson, char *& result)
{
//根据接收到的数据patientinfoJson 返回
m_strReply = ("222222333333");
//这里发信号与Http服务器的处理方式一致
::SendMessage(m_hWndParent, m_nMsgType, (WPARAM)infoJson, (LPARAM)result);
result = const_cast<char*>(m_strReply.c_str());
return SOAP_OK;
}
接下来添加一个WebServer类,里面调用到gsoap生成的服务器类,与Http服务器类似,你可以在这个类的构造函数中直接开启服务器,也可以写一个StartServer接口在里面实现,例如
int http_get(struct soap *soap)
{
FILE *fd = NULL;
fopen_s(&fd, "E:\\AppHisCommunicate3\\AppHisCommunicate\\WebServer\\HisApp.wsdl", "rb");//open WSDL file to copy
if (NULL == fd)
{
return 404;//return HTTP not found error
}
soap->http_content = "text/xml"; //HTTP header with text /xml content
soap_response(soap, SOAP_FILE);
while (1)
{
size_t r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), fd);
if (!r)
{
break;
}
if (soap_send_raw(soap, soap->tmpbuf, r))
{
break; //cannot send, but little we can do about that
}
}
fclose(fd);
soap_end_send(soap);
return SOAP_OK;
}
//开启服务器,建议在线程中使用
void WebServer::StartServer()
{
if (nullptr == m_pService)
{
m_pService = new xxxService(m_soap);
//这里是自己添加的接口,方便发送信号
m_pService->SetParentHWnd(m_hWndParent,m_nMsgType);
m_pService->fget = http_get;
//绑定服务器的Ip和端口号,例如127.0.0.1 8090
m_pService->bind(m_strIpAddress.c_str(), m_nPort, 100);//NULL,8090,100
int socketid = 0;
do
{
socketid = m_pService->accept();
if (socketid < 0)
{
m_pService->soap_stream_fault(std::cerr);
}
else
{
char* strMsg;
strMsg = ("accepted connection from IP=");
std::cout << *strMsg << m_pService->ip << " " << socketid;
}
int s = m_pService->serve();
if (s != 0)
{
m_pService->soap_stream_fault(std::cerr);
}
} while (1);
m_pService->reset();
m_pService->destroy();
}
}
此时服务器已经开启,浏览器输入 http://127.0.0.1:8090/HisApp?wsdl 可访问
还可以下载soapUI工具进行测试,可以参考SoapUI使用教程
应当注意的是,当gsoap同时生成客户端和服务端时需要先生成服务器,再生成客户端(命令Soapcpp2 -C -i xxx.h)(保持命名空间一致,在两个不同的文件夹下生成,否则生成的同名文件会覆盖),然后合并几个共同的文件(soapC.cpp、soapH.h、soapStub.h等),可以使用WinMerge工具,这里还要注意合并的时候几个宏的值不要重复。对服务器和客户端的接口要求不同时可以使用不同的.wsdl文件各自生成。
总结
总而言之,现在的各种库已经使得构建一个服务器变得越来越简洁,但是要做到更多的服务器相关功能,还是需要继续学习。