使用QWebSocketServer实现网络数据传输

本文介绍QWebSocket在安全模式和非安全模式下的实现细节,包括服务器端和客户端的具体实现流程,以及如何生成和使用SSL证书。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习记录贴,参考了许多文档,
部分内容参考自 https://www.jianshu.com/p/a9497de4cbff
内容参考自 https://blog.youkuaiyun.com/cqchengdan/article/details/97619665#commentBox
内容参考自 https://blog.youkuaiyun.com/cqchengdan/article/details/97619483
有问题可以相互请教,这里就是对今天学习内容的总结。

1.模式分类

QWebSocketServer具有安全模式和非安全模式两种类型。体现在初始化类型值的不同
QWebSocketServer::SecureMode 为安全模式。需要配置SSL安全证书,QWebSocketServer::NOSecureMode为非安全模式,直接进行明文传输。

2.非安全模式下QWebsocket实现

2.1 服务器端

1.初始化QWebSocketServer

void DataAnalysisClient::init()
{
    m_WebSocketServer = new QWebSocketServer("Server Name", QWebSocketServer::NoSecureMode, this);
}

2.实现服务器端接口

void DataAnalysisClient::onStartButtonClick()
{
    int i_port = 6000;
    m_WebSocketServer->listen(QHostAddress::Any, i_port);
    m_sendEdit->append(QString("服务启动!"));
}
void DataAnalysisClient::onStopButtonClick()
{
    m_WebSocketServer->close();
    for (int i=0;i<m_clients.size();i++)
    {
        m_clients.at(i)->close();
    }
    m_sendEdit->append(QString("服务停止!"));
}

void DataAnalysisClient::onSendButtonClick()
{
    QString msg = "服务端发送数据!";
    for (int i=0;i<m_clients.size();i++)
    {
        m_clients.at(i)->sendTextMessage(msg);
    }
}
void ACMSLIB::DataAnalysisClient::onNewConnection()
{
    // 读取到下一个等待连接的套接字,套接字需要显式删除
    pSocket = m_WebSocketServer->nextPendingConnection();
    connect(pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(processTextMessage(QString)));
    connect(pSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
    QString item = pSocket->peerAddress().toString();
    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");
    QString ip = m_WebSocketServer->serverAddress().toString();
    int port = m_WebSocketServer->serverPort();
    QString url = m_WebSocketServer->serverUrl().toString();
    m_sendEdit->append(QString("%1 + "" 新建连接:IP为%2").arg(time).arg(item));
    m_sendEdit->append(QString("IP:%1 + 端口号:%2 + url:%3").arg(ip).arg(port).arg(url));
    m_clients << pSocket;
}

//收到消息并显示
void DataAnalysisClient::processTextMessage(QString message)
{
    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");
    QString item = pSocket->peerAddress().toString();
    m_recEdit->append(time + "" + item + "\n" + message);
}

void DataAnalysisClient::socketDisconnected()
{
    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz");
    QString item = pSocket->peerAddress().toString();
    m_recEdit->append(QString(time + "" + item + "\n" + "失去连接:IP为%1").arg(item));
    m_recEdit->append("\n");
}

2.2 客户端

1.初始化

    // 操作按钮
    connect(m_startBtn, SIGNAL(clicked(bool)), this, SLOT(connectToServer()));
    connect(m_closeBtn, SIGNAL(clicked(bool)), this, SLOT(stopClicked()));
    connect(m_sendBtn, SIGNAL(clicked(bool)), this, SLOT(onSendButtonClicked()));
    // 数据传输
    connect(&m_websocket, SIGNAL(connected()), this, SLOT(onconnected()));
    connect(&m_websocket, SIGNAL(disconnected()), this, SLOT(closeConnection()));
    connect(&m_websocket, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString)));

2.接口实现

void Widget::connectToServer()
{
    QString IP= "***.***.***.***";
    QString prot = "6000";
    QString path = QString("wss://%1:%2").arg(IP).arg(prot);
    QUrl url = QUrl(path);
    m_websocket.open(url);
    // 错误提示
    connect(&m_websocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(readError(QAbstractSocket::SocketError)));
}

void Widget::onTextMessageReceived(const QString &message)
{
    m_recEdit->append("-----------------------------1");
    QString time = current_date_time->currentDateTime().toString("yyyy.MM.dd hh:mm:ss.zzz ddd");
    m_recEdit->append(time + "\n" + message);
}

void Widget::closeConnection()
{
}

void Widget::stopClicked()
{
    m_websocket.close();
}

void Widget::onconnected()
{
    m_recEdit->append("连接成功");
}

void Widget::onSendButtonClicked()
{
    QString msg = "客户端发送数据";
    m_websocket.sendTextMessage(msg);
}

void Widget::onCleanButtonClicked()
{
}

void Widget::readError(QAbstractSocket::SocketError err)
{
    qDebug () << "连接Error!";
}

3.安全模式下QWebsocket实现

3.1 证书准备

QWebsocket安全模式需要对SSL进行验证,QT本身不支持SSL验证,手动下载openSSL

3.2 生成证书

下载安装好后,打开openssl.exe

3.2.1 生成密钥

genrsa -des3 -out server.key 2048

说明:生成rsa私钥,des3算法,2048位强度,server.key是秘钥文件名。
注意:生成私钥,需要提供一个至少4位的密码。

3.2.2 生成CSR(证书签名请求)

req -new -key server.key -out server.csr

说明:需要依次输入国家,地区,城市,组织,组织单位,Common Name和Email。其中Common Name,可以写自己的名字或者域名,如果要支持https,Common Name应该与域名保持一致,否则会引起浏览器警告。

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:IT
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:IT
Email Address []:123456789@qq.com

3.2.3 删除私钥中的密码

rsa -in server.key -out server_no.key

备注:必须操作,不删除的话,在不鉴定客户端证书的情况下也无法连接成功,
在3.2.1创建私钥的过程中,由于必须要指定一个密码。而这个密码会带来一个副作用,那就是在每次Apache启动Web服务器时,都会要求输入密码,这显然非常不方便。要删除私钥中的密码,操作如下:

3.2.4 生成自签名证书

x509 -req -days 365 -in server.csr -signkey server_no.key -out server.crt

由此得到了签名证书server.crt和私钥server_no.key

3.3 设计服务端

在原有使用非安全模式的WebsockeServer服务的基础上添加一些签名证书和验证
1.初始化QWebSocketServer

void DataAnalysisClient::init()
{
    m_WebSocketServer = new QWebSocketServer("Server Name", QWebSocketServer::SecureMode, this);
    //
    QFile certFile(":server.crt");
    QFile keyFile(":server.key");
    if(!certFile.open(QIODevice::ReadOnly)){
        qDebug() << "certfile open failed...";
    }
    if(!keyFile.open(QIODevice::ReadOnly)){
        qDebug() << "keyfile open failed...";
    }
    QSslCertificate cert(&certFile, QSsl::Pem);
    QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem);
    certFile.close();
    keyFile.close();
    QSslConfiguration conf = m_WebSocketServer->sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setLocalCertificate(cert);
    conf.setPrivateKey(key);
    conf.setProtocol(QSsl::TlsV1SslV3);
    m_WebSocketServer->setSslConfiguration(conf);
}

3.4 设计客户端

void Widget::connectToServer()
{
    QString IP= "***.***.***.***";
    QString prot = "6000";
    QString path = QString("wss://%1:%2").arg(IP).arg(prot);
    QUrl url = QUrl(path);
    QSslConfiguration conf = m_websocket.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setProtocol(QSsl::TlsV1SslV3);
    m_websocket.setSslConfiguration(conf);
}

conf.setPeerVerifyMode(QSslSocket::VerifyNone);使用跳过证书验证的方式使用了SSL的安全传输,实现了数据加密功能。

3.5 使用证书验证

修改验证方式为QSslSocket::VerifyPeer,客户端初始化设置为
{
    QString IP= "***.***.***.***";
    QString prot = "6000";
    QString path = QString("wss://%1:%2").arg(IP).arg(prot);
    QUrl url = QUrl(path);
    QSslConfiguration conf = m_websocket.sslConfiguration();
    //conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setPeerVerifyMode(QSslSocket::VerifyPeer);
    conf.setProtocol(QSsl::TlsV1SslV3);
    m_websocket.setSslConfiguration(conf);
    //解决自签名证书不受信赖问题,VerifyNone不需添加以下代码
    QList<QSslCertificate> certlist = QSslCertificate::fromPath("F:/myself/server.crt");
    QSslError error(QSslError::SelfSignedCertificate, certlist.at(0));
    QList<QSslError> expectedErrors;
    expectedErrors.append(error);
    m_websocket.ignoreSslErrors(expectedErrors);
}

此方法暂时不适用,websocket的报error提示SocketError::SslHandshakeFailedError错误,暂时不知道原因。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值