Qt+webservice的多线程实现

原文地址::https://blog.csdn.net/qq_29176963/article/details/82776588

 

相关文章

1、Qt 之 QtSoap(访问WebService)----https://blog.csdn.net/liang19890820/article/details/51673800

2、【原创】一次QT WebService项目经历(十六问)----http://blog.chinaunix.net/uid-7776733-id-79186.html?_t=t

3、Qt调用WebService----https://www.cnblogs.com/findumars/p/5290140.html

4、QtWebService开发----https://download.csdn.net/download/qq_22098033/10899266

 

项目使用Qt搭建了一个数据库软件,需要远程访问公司的MES系统,使用webservice技术进行通信并以XML格式传输数据,为了使网络监听过程中不影响主线程程序的正常运行,我们需要将webservice相关功能放在新开的独立线程中。

本项目使用的是QtCreator(Qt5.5.0)+VisualStudio2013+gSOAP2.8搭建。其他版本只要版本是正确对应的,都大同小异。

文章目录
WebService相关概念
gSOAP总结
gSOAP简介
gSOAP结构
wsdl2h.exe
soapcpp2.exe
实例介绍
功能介绍
实例步骤
存在问题
Reference:
WebService相关概念
参见参考文献[3]。

WebService是一种跨编程语言和跨操作系统平台的远程调用技术。所谓跨编程语言和跨操作平台,就是说服务端和和客户端的搭建平台与编程语言可以都不相同。所谓远程调用,就是一台计算机a上 的一个程序可以调用到另外一台计算机b上的一个对象的方法。

其实可以从多个角度来理解WebService,从表面上看,WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过 Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。从深层次看,WebService是建立可互操作的分布式应用程序的新平台,是一个平台,是一套标准。它定义了应用程序如何在Web上实现互操作性,你可以用任何 你喜欢的语言,在任何你喜欢的平台上写Webservice ,只要我们可以通过Webservice标准对这些服务进行查询和访问。

WebService平台需要一套协议来实现分布式应用程序的创建。任何平台都有它的数据表示方法和类型系统。要实现互操作性,WebService平台必须提供一套标准的类型系统,用于沟通不同平台、编程语言和组件模型中的不同类型系统。Webservice平台必须提供一种标准来描述Webservice,让客户可以得到足够的信息来调用这个Webservice。最后,我们还必须有一种方法来对这个Webservice进行远程调用,这种方法实际是一种远程过程调用协议(RPC)。为了达到互操作性,这种RPC协议还必须与平台和编程语言无关。

XML+XSD,SOAP和WSDL就是构成WebService平台的三大技术。

XML+XSD
WebService采用HTTP协议传输数据,采用XML格式封装数据(即XML中说明调用远程服务对象的哪个方法,传递的参数是什么,以及服务对象的 返回结果是什么),XML是WebService平台中表示数据的格式,其优点在于它既是平台无关又是厂商无关的。
XML解决了数据表示的问题,但它没有定义一套标准的数据类型,更没有说怎么去扩展这套数据类型。例如整形数到底代表什么?16位,32位,64位?这些细节对实现互操作性很重要。XML Schema(XSD)就是专门解决这个问题的一套标准。它定义了一套标准的数据类型,并给出了一种语言来扩展这套数据类型。WebService平台就是用XSD来作为其数据类型系统的。当你用某种语言(如VB.NET或C#)来构造一个Webservice时,为了符合WebService标准,所有你使用的数据类型都必须被转换为XSD类型。你用的工具可能已经自动帮你完成了这个转换,但你很可能会根据你的需要修改一下转换过程。

SOAP
SOAP是"简单对象访问协议",SOAP协议 = HTTP协议 + XML数据格式。
WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议。SOAP提供了标准的RPC方法来调用WebService。
SOAP协议定义了SOAP消息的格式,SOAP协议是基于HTTP协议的,SOAP也是基于XML和XSD的,XML是SOAP的数据编码方式。打个比喻:HTTP就是普通公路,XML就是中间的绿色隔离带和两边的防护栏,SOAP就是普通公路经过加隔离带和防护栏改造过的高速公路。

WSDL
好比我们去商店买东西,首先要知道商店里有什么东西可买,然后再来购买,商家的做法就是张贴广告海报。 WebService也一样,WebService客户端要调用一个WebService服务,首先要有知道这个服务的地址在哪,以及这个服务里有什么方法可以调用,所以WebService务器端首先要通过一个WSDL文件来说明自己家里有啥服务可以对外调用,服务是什么(服务中有哪些方法,方法接受的参数是什么,返回值是什么),服务的网络地址用哪个url地址表示,服务通过什么方式来调用。
WSDL(Web Services Description Language)就是这样一个基于XML的语言,用于描述WebService及其函数、参数和返回值。它是WebService客户端和服务器端都能理解的标准格式。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的Webservice生成WSDL文档,又能导入WSDL文档,生成调用相应WebService的代理类代码。
WSDL文件保存在Web服务器上,通过一个url地址就可以访问到它。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址。 WebService服务提供商可以通过两种方式来暴露它的WSDL文件地址:1.注册到UDDI服务器,以便被人查找;2.直接告诉给客户端调用者。

gSOAP总结
gsoap概念:是一种能够把C/C++语言的接口转换成基于soap协议的webservice服务的工具。从官网的说明文档可知gSOAP可以为我们完成以下工作:

1、自动生成C和C++源代码,以使用和部署XML、JSON REST API以及SOAP/XML API;
2、使用gSOAP的快速XML流处理模型进行XML解析和验证,实现实现可移植的快速的和精简的API,每秒处理10K+消息仅需几KB代码和数据;
3、将WSDL转换为有效的C或C++源代码以使用或部署XML Web服务;
4、将XML schemas(XSD)转换为高效的C或C++源代码,以使用gSOAP全面的XML schemas功能覆盖来使用或部署XML REST API;
5、为WSDL和/或XSD定义的大型复杂XML规范生成高效的C和C ++代码,例如eBay,ONVIF,HL7,FHIR,HIPAA 5010,CDISC,XMPP XEP,TR-069,AWS,EWS,ACORD,ISO 20022和SWIFT,FixML,XBRL,OTA,IATA NDC,FedEx等(您甚至可以将多个组合在一起);
6、安全的XML处理过程:gSOAP不容易受到大部分XML攻击手段的攻击;
7、使用强大的XML数据绑定准确地序列化XML中的C和C++数据,这有助于通过静态类型快速开发类型安全的API,以避免运行时错误;
8、在WSDL和XSD文件上使用wsdl2h选项-O2或-O4进行“schema slicing”,通过自动删除未使用的schema类型(WSDL和XSD根目录中无法访问的类型)来优化XML代码库的大小;
9、使用和部署JSON REST API;
10、使用SAML令牌安全地使用HTTP/S,WS-Security和WS-Trust来使用和部署API;
11、使用测试信使CLI测试您的SOAP/XML API,它会自动生成基于WSDL的完整和随机化的SOAP/XML消息(使用带有选项-g的wsdl2h和soapcpp2)来测试Web服务API和客户端;
12、使用gSOAP Apache模块在Apache Web服务器中部署API,在IIS中使用gSOAP ISAPI扩展部署API;
13、使用gSOAP cURL插件和gSOAP WinInet插件来使用API;
14、符合XML的行业标准,包括XML REST和SOAP,WSDL,MIME,MTOM,DIME,WS-Trust,WS-Security(集成了WS-Policy和WS-SecurityPolicy),WS-ReliableMessaging,WS-Discovery,WS-Addressing等;

gSOAP简介
gSOAP一种跨平台的开源的C/C++软件开发工具包。生成C/C++的RPC代码,XML数据绑定,对SOAP Web服务和其他应用形成高效的具体架构解析器,它们都受益于一个XML接口。 这个工具包提供了一个全面和透明的XML数据绑定解决方案,Autocoding节省大量开发时间来执行SOAP/XML Web服务中的C/C++。此外,使用XML数据绑定大大简化了XML自动映射。应用开发人员不再需要调整应用程序逻辑的具体库和XML为中心的数据。

gSOAP结构
使用gSOAP首先需要用到了两个工具就是../gsoap-2.8/gsoap/bin/win32/wsdl2h.exe和../gsoap-2.8/gsoap/bin/win32/soapcpp2.exe,用于自动生成包含接口的c/c++源文件。

wsdl2h.exe
该工具可以根据输入的wsdl或XSD或URL产生相应的C/C++形式的.h供soapcpp2.exe使用。示例如下:

新建一个文件夹,将wsdl2h.exe(和.wsdl文件)放入,命令行进入当前路径后输入以下命令:

wsdl2h [options] XSD and WSDL files ...

wsdl2h -o file.h file1.wsdl
wsdl2h -o file.h http://www.genivia.com/calc.wsdl
1
2
3
4
根据WSDL自动生成file.h头文件,以供soapcpp2.exe使用。

wsdl2h主要的运行选项如下:

选项    描述
-a    对匿名类型产生基于顺序号的结构体名称
-b    提供单向响应消息的双向操作(双工)
-c    生成c代码
-c++    生成c++代码
-c++11    生成c++11代码
-d    使用DOM填充xs:any和xsd:anyType元素
-D    使用指针使具有默认值的属性成员可选
-e    不要限定枚举名称,此选项用于向后兼容gSOAP 2.4.1及更早版本,该选项不会生成符合WS-I Basic Profile 1.0a的代码。
-f    为schema扩展生成平面C++类层次结构
-g    生成全局顶级元素声明
-h    显示帮助信息
-I path    包含文件时指明路径,相当于#import
-i    不导入(高级选项)
-j    不生成SOAP_ENV__Header和SOAP_ENV__Detail定义
-k    不生成SOAP_ENV__Header mustUnderstand限定符
-l    在输出中包含许可证信息
-m    使用xsd.h模块导入基元类型
-N name    用name 来指定服务命名空间的前缀
-n name    用name 作为命名空间的前缀取代缺省的ns
-O1    通过省略重复的选择/序列成员来优化
-O2    优化-O1并省略未使用的模式类型(从根目录无法访问)
-o file    输出文件名
-P    不要创建从xsd__anyType继承的多态类型
-p    创建从base xsd__anyType继承的多态类型,当WSDL包含多态定义时,会自动执行此操作
-q name    使用name作为所有声明的C++命名空间
-R    在WSDL中为REST绑定生成REST操作
-r host[:port[:uid:pwd]]    通过代理主机,端口和代理凭据连接
-r:uid:pwd    连接身份验证凭据(验证身份验证需要SSL)
-s    不生成STL代码(没有std :: string和没有std :: vector)
-t file    使用类型映射文件而不是默认文件typemap.dat
-U    将Unicode XML名称映射到UTF8编码的Unicode C/C++标识符
-u    不产生工会unions
-V    显示当前版本并退出
-v    详细输出
-W    抑制编译器警告
-w    始终在响应结构中包装响应参数
-x    不生成_XML any/anyAttribute可扩展性元素
-y    为结构和枚举生成typedef同义词
soapcpp2.exe
该工具是一个根据.h文件生成若干支持webservice代码文件生成工具,生成的代码文件包括webservice客户端和服务端的实现框架,XML数据绑定等,具体说明如下:

新建一个文件夹,将soapcpp2.exe和.h文件放入,命令行进入当前路径后输入以下命令:

soapcpp2 [options] header_file.h

soapcpp2 mySoap.h
soapcpp2 -c -r -CL calc.h
1
2
3
4
会生成如下c类型文件:
[外链图片转存失败(img-WtNYPDjL-1567667757111)(https://github.com/KunBB/MarkdownPhotos/blob/master/QtWebservice/Figure_1.jpg?raw=true)]

使用命令-i会生成如下c++类型文件:
[外链图片转存失败(img-x8rFsHgR-1567667757111)(https://github.com/KunBB/MarkdownPhotos/blob/master/QtWebservice/Figure_2.jpg?raw=true)]

文件    描述
soapStub.h    根据输入的.h文件生成的数据定义文件,一般我们不直接引用它
soapH.h    所有客户端服务端都应包含的主头文件
soapC.cpp    指定数据结构的序列化和反序列化方法
soapClient.cpp    用于远程操作的客户端存根例程
soapServer.cpp    服务框架例程
soapClientLib.cpp    客户端存根与本地静态(反)序列化器结合使用
soapServerLib.cpp    服务框架与本地静态(反)序列化器结合使用
soapXYZProxy.h    使用选项-i:c++服务端对象(与soapC.cpp和soapXYZProxy.cpp链接)
soapXYZProxy.cpp    使用选项-i:客户端代码
soapXYZService.h    使用选项-i:server对象(与soapC.cpp和soapXYZService.cpp链接)
soapXYZService.cpp    使用选项-i:服务端代码
.xsd    ns.XSD由XML Schema生成,ns为命名空间前缀名,我们可以看看是否满足我们的协议格式(如果有此要求)
.wsdl    ns.wsdl由WSDL描述生成
.xml    生成了几个SOAP/XML请求和响应文件。即满足webservice定义的例子message(实际的传输消息),我们可以看看是否满足我们的协议格式(如果有此要求)
.nsmap    根据输入soapcpp2.exe的头文件中定义的命名空间前缀ns生成ns.nsmap,该文件包含可在客户端和服务端使用的命名空间映射表
soapcpp的主要运行选项如下:

选项    描述
-1    生成SOAP 1.1绑定
-2    生成SOAP 1.2绑定
-0    没有SOAP绑定,使用REST
-C    仅生成客户端代码
-S    仅生成服务端代码
-T    生成服务端自动测试代码
-Ec    为深度数据复制生成额外的例程
-Ed    为深度数据删除生成额外的例程
-Et    使用walker函数为数据遍历生成额外的例程
-L    不生成soapClientLib / soapServerLib
-a    使用SOAPAction和WS-Addressing来调用服务端操作
-A    要求SOAPAction调用服务端操作
-b    序列化字节数组char [N]为字符串
-c    生成纯C代码
-d    保存到指定目录中
-e    生成SOAP RPC编码样式绑定
-f N    多个soapC文件,每个文件有N个序列化程序定义(N≥10)
-h    打印一条简短的用法消息
-i    生成从soap struct继承的服务代理类和对象
-j    生成可以共享soap结构的C++服务代理类和对象
-I    包含其他文件时使用,指明 < path > (多个的话,用`:'分割),相当于#import ,该路径一般是gSOAP目录下的import目录,该目录下有一堆文件供soapcpp2生成代码时使用
-l    生成可链接模块(实验)
-m    为MEX编译器生成Matlab代码
-n    用于生成支持多个客户端和服务器端
-p    生成的文件前缀采用< name > ,而不是缺省的 “soap”
-q    使用name作为c++所有声明的命名空间
-r    生成soapReadme.md报告
-s    生成的代码在反序列化时,严格检查XML的有效性
-t    生成的代码在发送消息时,采用xsi:type方式
-u    通过抑制XML注释来取消注释WSDL / schema输出中的注释
-V    显示当前版本并退出
-v    详细输出
-w    不生成WSDL和schema文件
-x    不生成示例XML消息文件
-y    在示例XML消息中包含C / C ++类型访问信息
实例介绍
功能介绍
1、客户端能够向服务端发送字符串数据;
2、服务端能够接收到客户端发送的字符串数据;
3、服务端对字符串数据解析、查重并录入数据库;
4、软件窗口显示连接状态。

最终效果如下所示:
点击“开始连接”按钮:
[外链图片转存失败(img-lLGL2EqX-1567667757112)(https://github.com/KunBB/MarkdownPhotos/blob/master/QtWebservice/Figure_4.png?raw=true)]

从客户端接收到一组数据后:
[外链图片转存失败(img-FErlePrT-1567667757112)(https://github.com/KunBB/MarkdownPhotos/blob/master/QtWebservice/Figure_5.png?raw=true)]

实例步骤
1. 生成源代码
由于没有.wsdl文件,因此我们跳过wsdl2h.exe这一步骤,手动编写供soapcpp2.exe使用的头文件mySoap.h:

//gsoap ns service name: sendMsg
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service namespace: urn:sendMsg

int ns__sendMsg(char* szMsgXml,struct nullResponse{} *out);
1
2
3
4
5
6
新建文件夹,将soapcpp2.exe和mySoap.h放入,打开命令后进入该目录下,运行命令soapcpp2 mySoap.h,生成如下文件:
[外链图片转存失败(img-noE3eTSN-1567667757112)(https://github.com/KunBB/MarkdownPhotos/blob/master/QtWebservice/Figure_3.jpg?raw=true)]

2. 客户端程序编写
在QtCreator中向客户端工程目录添加文件sendMsg.nsmap、soapH.h、soapStub.h、stdsoap2.h、soapC.cpp、soapClient.cpp、stdsoap2.cpp(stdsoap2.h和stdsoap2.cpp位于文件夹../gsoap-2.8/gsoap)。

客户端项目构建的是一个控制台程序,因此直接在main函数里进行编写代码:

#include <QCoreApplication>
#include "stdio.h"
#include "gservice.nsmap"
#include "soapStub.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    printf("The Client is runing...\n");

    struct soap *CalculateSoap = soap_new();

    char server_addr[] = "127.0.0.1:8080"; // url地址,IP号+设备端口号
    char* data = "flange,6,7,8,9,10,11,12,13"; // 所需传递的数据

    nullResponse result; // 用于sendMsg的空返回值
    int iRet = soap_call_ns__sendMsg(CalculateSoap,server_addr,NULL,data,&result); // 调用之前定义的ns__sendMsg方法(即服务端提供的方法)

    if ( iRet == SOAP_ERR){
        printf("Error while calling the soap_call_ns__sendmsg");
    }
    else
    {
        printf("Calling the soap_call_ns__add success。\n");
    }

    soap_end(CalculateSoap);
    soap_done(CalculateSoap);

    return a.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
3. 服务端程序编写
在QtCreator中向服务端工程目录添加文件sendMsg.nsmap、soapH.h、soapStub.h、stdsoap2.h、soapC.cpp、soapServer.cpp、stdsoap2.cpp。

由于服务端网络通信功能需要不断对端口进行监听,因此为避免影响软件其他功能的运行,在此需要新开一条线程。项目头文件源码如下:
socketconnect.h:

#ifndef SOCKETCONNECT_H
#define SOCKETCONNECT_H

#include <QWidget>
#include <QDebug>
#include <QVector>
#include "webservice_thread.h" // 多线程头文件
#include <QMessageBox>

namespace Ui {
class SocketConnect;
}

class SocketConnect : public QWidget
{
    Q_OBJECT

public:
    void setDbconn(QSqlDatabase *dbconn); // 主线程数据库
    explicit SocketConnect(QWidget *parent = 0);
    ~SocketConnect();

private slots:
    void pB_lj_clicked(); // 连接按钮
    void pB_dk_clicked(); // 断开连接按钮
    void linkState_accept(QVector<QString>); // 用于在窗口显示连接状态
    void data_accept(QVector<QString>); // 接收数据,检查后录入数据库

private:
    QSqlDatabase *dbconn;
    WebserviceThread *WebT;
    QVector<QString> v_linkstate;

    Ui::SocketConnect *ui;
};

#endif // SOCKETCONNECT_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
socketconnect.cpp:

#include "socketconnect.h"
#include "ui_socketconnect.h"
#include "scrollwidget.h"

SocketConnect::SocketConnect(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SocketConnect)
{
    ui->setupUi(this);

    ui->tabWidget->setTabText(1, QStringLiteral("法兰盘部件"));
    ui->tabWidget->setCurrentIndex(0);

    ui->lineEdit->setText("127.0.0.1"); // 暂时无用
    ui->lineEdit_2->setText("8080");
    ui->lineEdit_3->setText("helloworld");
    ui->lineEdit_4->setText("helloworld");
    ui->lineEdit_4->setEchoMode(QLineEdit::Password);

    ui->tableWidget_fla->setEditTriggers(QAbstractItemView::NoEditTriggers); // 表格不可编辑
    ui->tableWidget_fla->setSelectionBehavior(QAbstractItemView::SelectRows);

    ui->tableWidget_fla->setAlternatingRowColors(true);
    ui->tableWidget_fla->setStyleSheet("QTableView{alternate-background-color: rgb(183, 242, 238);}"); // 设置隔行换色

    connect(ui->pushButton_lj,SIGNAL(clicked(bool)),this,SLOT(pB_lj_clicked()));
    connect(ui->pushButton_dk,SIGNAL(clicked(bool)),this,SLOT(pB_dk_clicked()));
}

SocketConnect::~SocketConnect()
{
    delete ui;
}

void SocketConnect::setDbconn(QSqlDatabase *db){
    this->dbconn=db;
}

void SocketConnect::pB_lj_clicked()
{
    if(ui->lineEdit_3->text()=="helloworld"&&ui->lineEdit_4->text()=="helloworld"){
        WebT = new WebserviceThread(this);
        int port = ui->lineEdit_2->text().toInt();
        WebT->setParameters(port);
        connect(WebT,SIGNAL(linkState_send(QVector<QString>)),this,SLOT(linkState_accept(QVector<QString>)));
        connect(ui->pushButton_dk,SIGNAL(clicked(bool)),WebT,SLOT(stopclick_accept()));
        connect(WebT,SIGNAL(data_send(QVector<QString>)),this,SLOT(data_accept(QVector<QString>)));
        WebT->start(); // 进入线程
    }
    else{
        QMessageBox::critical(NULL, QString::fromLocal8Bit("警告"), QStringLiteral("账号或密码错误,无法连接!"), QMessageBox::Yes);
    }
}

void SocketConnect::linkState_accept(QVector<QString> link_state){
    ScrollWidget *s_linkstate = new ScrollWidget; // 自己定义的QWidget的子类,用于增加、更新、清空QList<QWidget*>

    QString linkstate;
    for(int i=0;i<link_state.size();i++){
        linkstate+=link_state[i];
    }
    v_linkstate.append(linkstate); // 存储线程文件传过来的连接状态

    for(int i=0;i<v_linkstate.size();i++){
        QLabel *label_linkstate = new QLabel(v_linkstate[i]);
        s_linkstate->addWidget(label_linkstate);
    }
    s_linkstate->updateWidget();
    ui->scrollArea->setWidget(s_linkstate); // 更新scrollarea
}

void SocketConnect::data_accept(QVector<QString> content){
    dbconn->open();

    QString dataClass = content[0]; // 第一个数据存储了数据类型信息
    content.erase(content.begin());

    //检查编号数据是否重复或为空
    dbconn->open();
    if(content[0].isEmpty()){
        QMessageBox::critical(NULL, QString::fromLocal8Bit("警告"), QStringLiteral("远程传输数据的编号为空,请检查并重新录入!"), QMessageBox::Yes);
        return;
    }
    QSqlQueryModel *model_sql = new QSqlQueryModel;
    model_sql->setQuery(QString("SELECT * FROM wbq.%1").arg(dataClass));

    for(int k=0 ;k<model_sql->rowCount(); k++){
        QModelIndex index = model_sql->index(k,0);
        if(content[0] == model_sql->data(index).toString()){
            QMessageBox::critical(NULL, QString::fromLocal8Bit("警告"), QStringLiteral("远程传输数据的编号已存在于数据库中,请检查并重新录入!"), QMessageBox::Yes);
            return;
        }
    }
    //结束检查

    if(dataClass=="flange"){
        ui->tabWidget->setCurrentIndex(1);

        //数据写入MySQL数据库
        QSqlQueryModel *model = new QSqlQueryModel;
        model->setQuery(QStringLiteral("INSERT INTO wbq.coordinator SET 编号=%1, ......").arg(content[0])......; // 输入数据
        dbconn->close();
        delete model;

        //数据显示到表格中
        int row = ui->tableWidget_fla->rowCount()+1;
        ui->tableWidget_fla->setRowCount(row);
        for(int i=0;i<content.size();i++){
            ui->tableWidget_fla->setItem(row-1,i,new QTableWidgetItem);
            ui->tableWidget_fla->item(row-1,i)->setText(content[i]);
        }
    }
    else{
        QMessageBox::critical(NULL, QString::fromLocal8Bit("警告"), QStringLiteral("信息格式有误或信息错误!"), QMessageBox::Yes);
    }
}

void SocketConnect::pB_dk_clicked()
{
    ui->scrollArea->takeWidget();

    QVector<QString> tmp;
    v_linkstate.swap(tmp);

    ui->tableWidget_fla->setRowCount(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
多线程头文件webservice_thread.h:

#ifndef WEBSERVICE_THREAD
#define WEBSERVICE_THREAD

#include<QtSql/QSql>
#include<QtSql/qsqlquerymodel.h>
#include<QtSql/QSqlQuery>
#include<QtSql/qsqldatabase.h>
#include<QSqlError>
#include<QStandardItem>

#include <QThread>
#include <QDebug>
#include <QVector>
#include <QMetaType>
#include <QWaitCondition>

class WebserviceThread: public QThread{
    Q_OBJECT

public:
    WebserviceThread(QObject *parent=0);
    ~WebserviceThread();
    void run();
    void setParameters(int);

signals:
    void linkState_send(QVector<QString>); // 向主线程发送连接状态
    void data_send(QVector<QString>); // 向主线程发送从客户端接收的数据

private slots:
    void stopclick_accept(); // 改变runstate

private:
    QVector<QString> readXML(QString);
    int runstate; // 用于终止循环
    int nPort;
};

#endif // WEBSERVICE_THREAD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
多线程文件webservice_thread.cpp:

#include "webservice_thread.h"
#include <sstream>
//gsoap文件
#include"gservice.nsmap"
#include"soapH.h"
#include"soapStub.h"
#include"stdsoap2.h"
#include"stdsoap2.cpp"
#include"soapC.cpp"
#include"soapServer.cpp"

QString Msg; // 存储客户端发送过来的数据

WebserviceThread::WebserviceThread(QObject *parent):QThread(parent){
    qRegisterMetaType<QVector<QString>>("QVector<QString>");
    runstate=0; //置1时停止网络循环
}

WebserviceThread::~WebserviceThread(){

}

void WebserviceThread::setParameters(int port){
    nPort=port;
}

int http_get_wbq(soap *);
void WebserviceThread::run(){
    struct soap wbq_soap;
    soap_init(&wbq_soap);
    wbq_soap.fget = http_get_wbq; // 网上有人说如果要传输的数据量大的话应该用http post
    int nMaster = (int)soap_bind(&wbq_soap,NULL,nPort,100); // 端口绑定
    if(nMaster<0)
        soap_print_fault(&wbq_soap,stderr);
    else{
        QVector<QString> link_state_1;
        link_state_1.append(QString("Socket connection successful: master socket = "));
        link_state_1.append(QString::number(nMaster, 10));
        emit linkState_send(link_state_1); // 发射初始连接后的状态信息

        for(int i=0;;i++){
            int nSlave = (int)soap_accept(&wbq_soap); // 端口监听,获取客户端连接信息
            if(nSlave<0){
                soap_print_fault(&wbq_soap,stderr);
                break;
            }

            QVector<QString> link_state_2;
            link_state_2.append(QString("Times "));
            link_state_2.append(QString::number(i, 10));
            link_state_2.append(QString(". Accepted connection from "));
            link_state_2.append(QString::number(int((wbq_soap.ip>>24)&0xFF), 10));
            link_state_2.append(QString("."));
            link_state_2.append(QString::number(int((wbq_soap.ip>>16)&0xFF), 10));
            link_state_2.append(QString("."));
            link_state_2.append(QString::number(int((wbq_soap.ip>>8)&0xFF), 10));
            link_state_2.append(QString("."));
            link_state_2.append(QString::number(int((wbq_soap.ip)&0xFF), 10));
            link_state_2.append(QString(": slave socket = "));
            link_state_2.append(QString::number(nSlave, 10));
            if(!runstate)
                emit linkState_send(link_state_2); // 发射客户端连接信息

            if(runstate)//点击断开连接后的下一次循环可以ping通,但无法调用服务端的sendMsg方法,即数据无法传送,不会造成数据丢失。
                break;

            if(soap_serve(&wbq_soap)!=SOAP_OK)
                soap_print_fault(&wbq_soap,stderr);
            soap_destroy(&wbq_soap);
            soap_end(&wbq_soap);

            QVector<QString> data = readXML(Msg); // 解析数据
            emit data_send(data); // 向主线程传递解析后的数据
            Msg="";
        }
    }
    soap_done(&wbq_soap);
}

void WebserviceThread::stopclick_accept(){
    runstate = 1;
}

/*
此功能是按自己格式解析的
*/
QVector<QString> WebserviceThread::readXML(QString Msg){
    QVector<QString> data;
    QStringList msglist = Msg.split(",");
    for(int i=0;i<msglist.size();i++){
        data.append(msglist[i]);
    }
    return data;
}

int ns__sendMsg(struct soap *soap, char* smsg, nullResponse* result)
{
    Msg = QString(smsg);
    return SOAP_OK;
}

/*
用于在网页页面正常显示信息(百度得到的解决方案)
*/
int http_get_wbq(struct soap *soap){
    soap_response(soap, SOAP_HTML); // HTTP response header with text/html
    soap_send(soap, "<HTML>My Web server is operational.</HTML>");
    soap_end_send(soap);
    return SOAP_OK;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
存在问题
1、gSOAP中有对数据序列化与反序列化的功能,不应按自己的格式来传送数据;
2、断开连接按功能在问题,不能及时断开连接。点击“断开连接”按钮后,客户端需要再试图与服务端通信一次,服务端才能真正跳出循环,目前只能做到最后一次通信客户端报错,数据无法传送,不会造成数据丢失。
3、这种方法并没有使用XML传输数据,并不正规,仅供参考。

Reference:
[1] Qt中多线程的使用:https://blog.csdn.net/mao19931004/article/details/53131872
[2] Qt使用多线程的一些心得——1.继承QThread的多线程使用方法:https://blog.csdn.net/czyt1988/article/details/64441443
[3] WebService学习总结(一)——WebService的相关概念:https://www.cnblogs.com/xdp-gacl/p/4048937.html
[4] gsoap使用总结:https://blog.csdn.net/byxdaz/article/details/51679117

本博客与https://xuyunkun.com同步更新


————————————————
版权声明:本文为CSDN博主「困比比」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_29176963/article/details/82776588

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值