WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程。
默认情况下:WebSocket 协议使用 80 端口;
WebSocket目前支持两种统一资源标志符ws和wss,类似于HTTP和HTTPS。
如: ws://example.com:80/some/path
如: ws://127.0.0.1:45678 即ip+端口
因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开), 因此websocket应运而生。
1)较少的控制开销:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小;
2)更强的实时性:由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少;
3)保持连接状态:与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息;
4)更好的二进制支持:WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容;
5)可以支持扩展:WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。
要使用 Qt 的 WebSocket 模块,先在 pro 文件中加上 websockets.
QT += websockets
QHostAddress localAddress() const;
quint16 localPort() const;
QUrl requestUrl() const;
QNetworkRequest request() const;
当有需要接收的数据时,会发出该信号
void textMessageReceived(const QString &message); //字符串的方式接收数据信息
void binaryMessageReceived(const QByteArray &message);//二进制的方式接收数据信息
当需要发送数据时,调用下面的发送函数
qint64 sendTextMessage(const QString &message); //字符串的方式发送数据信息
qint64 sendBinaryMessage(const QByteArray &data);//二进制的方式发送数据信息
客户端断开连接,收到断开信号:
void disconnected();
QWebSocketServer以 QTcpServer 为模型,并且行为相同。所以,如果你知道如何使用 QTcpServer,你就知道如何使用 QWebSocketServer.
监听连接。 如果端口为 0,则自动选择一个端口。 如果地址是 QHostAddress::Any,则服务器将侦听所有网络接口。
m_pWebSocketServer->listen(QHostAddress::LocalHost, mPort);//端口号
客户端直接使用QWebSocket的函数进行操作即可
开发环境:QT5.15.2 MSVC 2019 64Bit
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void sendTextMessageSignal(QString msg);
private slots:
void on_pushButton_Listen_clicked();
void OnNewConnectionSlot();
void OnTextReceivedSlot(QString msg);
void OndisconnectedSlot();
void on_pushButton_send_clicked();
private:
Ui::Widget *ui;
QWebSocketServer *server;
QList m_clientList;
QMap m_clientStatus;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
server = new QWebSocketServer("testserver",QWebSocketServer::NonSecureMode,this);
connect(server,&QWebSocketServer::newConnection,this,&Widget::OnNewConnectionSlot);
ui->lineEdit_address->setText("Any");
ui->lineEdit_port->setText("45678");
}
Widget::~Widget()
{
qDeleteAll(m_clientList);
server->close();
delete ui;
}
void Widget::on_pushButton_Listen_clicked()
{
QHostAddress address;
if(ui->lineEdit_address->text()=="Any")
{
address=QHostAddress::Any;
}
else
{
address=QHostAddress(ui->lineEdit_address->text());
}
int nPort = ui->lineEdit_port->text().toInt();
bool bListen = server->listen(address,nPort);
if(bListen)
ui->pushButton_Listen->setEnabled(false);
}
void Widget::OnNewConnectionSlot()
{
QWebSocket *client = server->nextPendingConnection();
m_clientList.append(client);
qDebug() << "Client:" << client->peerAddress().toString() << client->peerName() <peerPort() ;
QString strstatus = "ip:" + client->peerAddress().toString() + " 端口:"+ QString::number(client->peerPort()) + "连接成功\n";
m_clientStatus.insert(client,strstatus);
ui->textEdit_clientlist->insertPlainText(strstatus);
ui->textEdit_clientlist->
connect(client,&QWebSocket::textMessageReceived,this,&Widget::OnTextReceivedSlot);
connect(this,&Widget::sendTextMessageSignal,client,&QWebSocket::sendTextMessage);//给所有客户端发送数据
connect(client,&QWebSocket::disconnected,this,&Widget::OndisconnectedSlot);
}
void Widget::OnTextReceivedSlot(QString msg)
{
ui->textEdit_recv->setText(msg);
QWebSocket *pClient = qobject_cast(sender());
pClient->sendTextMessage("回答" + msg);
}
void Widget::OndisconnectedSlot()
{
QWebSocket *pClient = qobject_cast(sender());
m_clientStatus.remove(pClient);
ui->textEdit_clientlist->clear();
QList textlist = m_clientStatus.values();
for(auto text : textlist)
{
ui->textEdit_clientlist->insertPlainText(text);
}
m_clientList.removeAll(pClient);
pClient->deleteLater();
}
void Widget::on_pushButton_send_clicked()
{
QString msg = ui->textEdit_send->toPlainText();
emit sendTextMessageSignal(msg);
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_connect_clicked();
void onConnectedSlot();
void ondisconnectedSlot();
void onTextReceivedSlot(QString message);
void on_pushButton_send_clicked();
void on_pushButton_disconnect_clicked();
private:
Ui::Widget *ui;
QWebSocket *m_ClientSocket;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
m_ClientSocket = new QWebSocket();
m_ClientSocket->setParent(this);
ui->label_connet->setText("尚未连接服务端");
ui->pushButton_connect->setEnabled(true);
ui->pushButton_disconnect->setEnabled(false);
ui->lineEdit_url->setText("ws://127.0.0.1:45678");
}
Widget::~Widget()
{
m_ClientSocket->close();
delete ui;
}
void Widget::on_pushButton_connect_clicked()
{
QUrl url(ui->lineEdit_url->text());
m_ClientSocket->open(url);
connect(m_ClientSocket, &QWebSocket::connected, this, &Widget::onConnectedSlot);
connect(m_ClientSocket, &QWebSocket::disconnected, this, &Widget::ondisconnectedSlot);
}
void Widget::onConnectedSlot()
{
ui->pushButton_connect->setEnabled(false);
ui->pushButton_disconnect->setEnabled(true);
QString strstatus = "连接服务端" + ui->lineEdit_url->text() + "成功";
ui->label_connet->setText(strstatus);
connect(m_ClientSocket, &QWebSocket::textMessageReceived, this, &Widget::onTextReceivedSlot);
}
void Widget::ondisconnectedSlot()
{
QString strstatus = "断开服务端" + ui->lineEdit_url->text() + "连接";
ui->label_connet->setText(strstatus);
ui->pushButton_connect->setEnabled(true);
ui->pushButton_disconnect->setEnabled(false);
}
void Widget::onTextReceivedSlot(QString message)
{
ui->textEdit_recv->setText(message);
}
void Widget::on_pushButton_send_clicked()
{
QString message = ui->textEdit_send->toPlainText();
m_ClientSocket->sendTextMessage(message);
}
void Widget::on_pushButton_disconnect_clicked()
{
m_ClientSocket->close();
}