Qt网络编程之服务端
TCP(传输控制协议)是一种可靠的、面向流的、面向连接的传输协议。它特别适合连续的数据传输。
1. 主要类和函数
1.1 QTcpServer
监听函数:
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
Tells the server to listen for incoming connections on address address and port port. If port is 0, a port is chosen automatically. If address is QHostAddress::Any, the server will listen on all network interfaces.
Returns true on success; otherwise returns false.
告知服务器在地址 address 和端口 port 上监听传入的连接。如果 port 为 0,则自动选择一个端口。如果 address 为 QHostAddress::Any,服务器将在所有网络接口上进行监听。
成功时返回 true;否则返回 false。
新的链接信号:
[signal] void QTcpServer::newConnection();
This signal is emitted every time a new connection is available.
每当有新的连接可用时,就会发出此信号。
1.2 QTcpSocket
收到数据信号:
[signal] void QIODevice::readyRead();
This signal is emitted once every time new data is available for reading from the device’s current read channel. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device.
readyRead() is not emitted recursively; if you reenter the event loop or call waitForReadyRead() inside a slot connected to the readyRead() signal, the signal will not be reemitted (although waitForReadyRead() may still return true).
Note for developers implementing classes derived from QIODevice: you should always emit readyRead() when new data has arrived (do not emit it only because there’s data still to be read in your buffers). Do not emit readyRead() in other conditions.
每当设备当前读取通道有新数据可供读取时,就会发出此信号。只有在有新数据可用时,例如网络套接字上到达新的网络数据负载,或者设备上追加了新的数据块时,才会再次发出此信号。
readyRead() 不会递归发出;如果您在与 readyRead() 信号连接的槽中重新进入事件循环或调用 waitForReadyRead(),则不会重新发出该信号(尽管 waitForReadyRead() 仍可能返回 true)。
注意:对于实现派生自 QIODevice 的类的开发人员:当有新数据到达时,您应始终发出 readyRead()(不要仅仅因为缓冲区中仍有数据可读就发出)。在其他情况下不要发出 readyRead()。
断开链接信号:
[signal] void QAbstractSocket::disconnected();
This signal is emitted when the socket has been disconnected.
Warning: If you need to delete the sender() of this signal in a slot connected to it, use the deleteLater() function.
该信号在套接字断开连接时发出。
警告:如果您需要删除连接到该信号的插槽中的sender(),请使用deleteLater()函数。
2. 程序示例
编写一个服务端数据,界面显示客户端的链接信息,其中一个客户端发来的数据,转发给其他客户端;
程序代码:
// .h 文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>
#include <QTcpServer>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();public slots:void SlotNewConnection();void SlotReceiveDataFromClient();void SlotClientDisconnected();private:Ui::MainWindow *ui;QTcpServer* m_pTcpServer; // 服务端类QVector<QTcpSocket*> m_vecSocket; // 管理链接的客户端socket对象
};
#endif // MAINWINDOW_H// .cpp 文件
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QVariant>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow), m_pTcpServer(new QTcpServer(this))
{ui->setupUi(this);connect(m_pTcpServer, &QTcpServer::newConnection, this, &MainWindow::SlotNewConnection);m_pTcpServer->listen(QHostAddress::Any, 8088);
}MainWindow::~MainWindow()
{delete ui;
}// 新的链接
void MainWindow::SlotNewConnection()
{QTcpSocket* pSocket = m_pTcpServer->nextPendingConnection();connect(pSocket, &QTcpSocket::readyRead, this, &MainWindow::SlotReceiveDataFromClient);connect(pSocket, &QTcpSocket::disconnected, this, &MainWindow::SlotClientDisconnected);QString strClientInfo = QString("Address:%1, Port:%2").arg(pSocket->peerAddress().toString()).arg(pSocket->peerPort());ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("客户端【%1】链接本服务器").arg(strClientInfo));ui->listWidget->addItem(strClientInfo);m_vecSocket.append(pSocket);
}// 某个客户端发来信息
void MainWindow::SlotReceiveDataFromClient()
{QTcpSocket* pSocket = dynamic_cast<QTcpSocket*>(sender());QByteArray byteArr = pSocket->readAll();if (byteArr.isEmpty())return;for (auto& pTempSocket : m_vecSocket){// 非发来的客户端便转发出去if (pTempSocket != pSocket)pTempSocket->write(byteArr, byteArr.length());}
}// 断开链接
void MainWindow::SlotClientDisconnected()
{QTcpSocket* pSocket = dynamic_cast<QTcpSocket*>(sender());auto pFind = std::find(m_vecSocket.begin(), m_vecSocket.end(), pSocket);if (pFind != m_vecSocket.end())m_vecSocket.erase(pFind);QString strClientInfo = QString("Address:%1, Port:%2").arg(pSocket->peerAddress().toString()).arg(pSocket->peerPort());for (int i = 0; i != ui->listWidget->count(); ++i){QListWidgetItem* pItem = ui->listWidget->item(i);QString strTempClientInfo = pItem->text();if (strTempClientInfo == strClientInfo){ui->listWidget->removeItemWidget(pItem);delete pItem;return;}}
}
断开一个链接后