QT 網絡編程
TCP 編程
模塊引入
QT += network
頭文件
#include <QTcpServer> // TCP服務器端使用
#include <QTcpSocket> // TCP服務器和客戶端都使用
編程流程
服務端
1)實例化 QTcpServer 對象 -----------------------------> socket
2)進入監聽狀態 ----> listen(QTcpServer類) // 不需要再綁定了----------->bind + listen
3)監測客戶端連接 ---- newConnection 信號(QTcpServer類)
----------------> 有新連接過來,server 就能收到 newConnection 信號
4)QTcpSocket *client <---- 獲得連接 ---- nextPendingConnection(QTcpServer類) ---->accept
5)連接對端接收信號 ------ readyRead(QTcpSocket類)
---------------------->如果對端有數據發送,server 就能收到 readyRead 信號
6)讀取客戶端消息 ------ readAll(QTcpSocket類) --------------------------> recv:讀取數據
7)發送數據 ------ write(QTcpSocket類) ----> send:發數據
8)關閉連接 ------ disconnectFromHost() -------------------> close
客戶端
1)實例化 QTcpSocket 對象;
2)連接服務器 ------ connectToHost ------> 接下來使用 waitForConnected 來判斷是否連接成功
3)連接對端接收信號 ------ readyRead 信號
4)發送數據 ------ write()
5)關閉連接 ------ disconnectFromHost()
💡 客戶端實現
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>
#include <QTcpSocket>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_connectBtn_clicked();void recvSlot();void on_sendBtn_clicked();
// void whetherConnectedSlot(); // 判斷是否連接成功,方法二private:Ui::Widget *ui;QTcpSocket *client;bool flag;
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客戶端");client = new QTcpSocket(this);QObject::connect(client, SIGNAL(readyRead()), this, SLOT(recvSlot()));// 判斷是否連接成功,方法二
// QObject::connect(client, SIGNAL(connected()), this, SLOT(whetherConnectedSlot()));
}Widget::~Widget()
{delete ui;
}// void Widget::whetherConnectedSlot() // 判斷是否連接成功,方法二
// {
// flag = true;
// }void Widget::on_connectBtn_clicked()
{client->connectToHost(ui->ipEdit->text(), ui->portEdit->text().toShort());// 判斷是否連接成功,方法一if (!client->waitForConnected(1000)) {qDebug() << "Failed to connect. ";return ;}qDebug() << "Connected successfully! ";ui->connectBtn->setText("斷開");
}void Widget::recvSlot()
{QByteArray buffer = client->readAll();ui->recvEdit->setText(QString::fromLocal8Bit(buffer));
}void Widget::on_sendBtn_clicked()
{QString buffer = ui->sendEdit->toPlainText();client->write(buffer.toLocal8Bit());
}
💡 服務器實現
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QtWidgets>
#include <QTcpServer>
#include <QTcpSocket>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private slots:void on_connectBtn_clicked();void connectSlot();void recvSlot();void on_sendBtn_clicked();private:Ui::Widget *ui;QTcpServer *server;QTcpSocket *client;
};#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);server = new QTcpServer(this);this->setWindowTitle("服務器");// 這個信號觸發,代表有客戶端連接QObject::connect(server, SIGNAL(newConnection()), this, SLOT(connectSlot()));
}Widget::~Widget()
{delete ui;
}void Widget::on_connectBtn_clicked()
{// 監聽是否有客戶端連入,不阻塞等待if (!server->listen(QHostAddress::Any, ui->portEdit->text().toUShort())){qDebug() << "Failed to listen. ";return ;}ui->connectBtn->setText("關閉");
}void Widget::connectSlot()
{// 接受新的客戶端連接client = server->nextPendingConnection();if (client == 0){qDebug() << "There are no pending connections. ";return ;}// 一定要等到接受連接后,再去做客戶端的信號連接QObject::connect(client, SIGNAL(readyRead()), this, SLOT(recvSlot()));
}void Widget::recvSlot()
{QByteArray buffer = client->readAll();// 編碼轉換:對方必須按照 GBK 格式發送ui->recvEdit->setText(QString::fromLocal8Bit(buffer));
}void Widget::on_sendBtn_clicked()
{QString buffer = ui->sendEdit->toPlainText(); // 從textEdit中獲取的內容一定是utf8編碼client->write(buffer.toLocal8Bit());
}
💡 服務器與客戶端交互
UDP 編程
模塊引入
QT += network
頭文件
#include
編程流程
1)實例化 QUdpSocket 對象 ------------------------------------------> socket
2)綁定地址、端口 ------ bind(QHostAddress::LocalHost,8888) -----> bind
3)收發報文 ------ readDatagram、writeDatagram ------------------> recvfrom/sendto
// 類和接口
bool QUdpSocket::hasPendingDatagrams() const;
qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0);
qint64 QUdpSocket::writeDatagram(const char * data, qint64 size, const QHostAddress & address, quint16 port);
實現一個聊天功能,使用 UDP 通信。首先通過一個登錄界面,輸入服務器的 IP 和端口,點擊登錄后,將 IP 和端口傳到聊天界面,然后通過 UDP 進行聊天(服務器)。
💡 服務器實現
chatpage.h
#ifndef CHATPAGE_H
#define CHATPAGE_H#include <QtWidgets>
#include <QUdpSocket>
#include "globalvalue.h"namespace Ui {
class ChatPage;
}class ChatPage : public QWidget
{Q_OBJECTpublic:explicit ChatPage(QWidget *parent = 0);~ChatPage();private:Ui::ChatPage *ui;QUdpSocket *socket;QHostAddress sender; // 定義對端的地址,以便后續發送使用quint16 senderPort;private slots:void readPendingDatagrams();void on_sendBtn_clicked();
};#endif // CHATPAGE_H
chatpage.cpp
#include "chatpage.h"
#include "ui_chatpage.h"ChatPage::ChatPage(QWidget *parent) :QWidget(parent),ui(new Ui::ChatPage)
{ui->setupUi(this);this->setWindowTitle("聊天");// 初始化一個 QUdpSocket 對象socket = new QUdpSocket(this);// 綁定服務器的地址和IP,客戶端省略此句socket->bind(QHostAddress(GlobalValue::ipaddr), GlobalValue::port);connect(socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}ChatPage::~ChatPage()
{delete ui;
}void ChatPage::readPendingDatagrams()
{// 如果udp緩沖區有報文數據的話while (socket->hasPendingDatagrams()){QByteArray datagram; // 初始化一個字節流緩沖區datagram.resize(socket->pendingDatagramSize()); // 重設緩沖區的大小socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 對方發送的字節流必須是GBK編碼ui->recvEdit->setText(QString::fromLocal8Bit(datagram));}
}
void ChatPage::on_sendBtn_clicked()
{// 如果已經接收過消息,那么sender和senderPort已經有對方的地址了socket->writeDatagram(ui->sendEdit->toPlainText().toLocal8Bit(), sender, senderPort);
}
globalvalue.h
#ifndef GLOBALVALUE_H
#define GLOBALVALUE_H#include <QString>class GlobalValue // 此類僅用于存放靜態變量
{
public:GlobalValue();static QString ipaddr;static quint16 port;
};#endif // GLOBALVALUE_H
globalvalue.cpp
#include "globalvalue.h"QString GlobalValue::ipaddr;
quint16 GlobalValue::port;GlobalValue::GlobalValue()
{
}
userver.h
#ifndef USERVER_H
#define USERVER_H#include <QtWidgets>
#include "chatpage.h"
#include "globalvalue.h"namespace Ui {
class UServer;
}class UServer : public QWidget
{Q_OBJECTpublic:explicit UServer(QWidget *parent = 0);~UServer();private slots:void on_pushButton_clicked();private:Ui::UServer *ui;ChatPage *chatting;
};#endif // USERVER_H
userver.cpp
#include "userver.h"
#include "ui_userver.h"UServer::UServer(QWidget *parent) :QWidget(parent),ui(new Ui::UServer)
{ui->setupUi(this);this->setWindowTitle("登錄");
}UServer::~UServer()
{delete ui;
}void UServer::on_pushButton_clicked()
{GlobalValue::ipaddr = ui->ipEdit->text();GlobalValue::port = ui->portEdit->text().toUShort();chatting = new ChatPage;chatting->show(); // 初始化一個新的界面,然后進行跳轉this->close();
}
運行效果如下: