Qt下的基本TCP网络通信流程
给大家讲一下如何使用Qt相关类的进行TCP通信。前置知识:c++基础、qt基础、网络基础
使用Qt提供的类进行基于TCP的套接字通信需要用到两个类:
QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接。
QTcpSocket:通信的套接字类,客户端、服务器端都需要使用。
这两个套接字通信类都属于网络模块network。所以需要加入对应的network模块。

其实无论哪个语言,通信的流程都是大差不差的

1.QTcpServer
QTcpServer类用于监听客户端连接以及和客户端建立连接,在使用之前先介绍一下这个类提供的一些常用API函数:
1.1构造函数
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);
设置监听
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
// 判断当前对象是否在监听, 是返回true,没有监听返回false
bool QTcpServer::isListening() const;
// 如果当前对象正在监听返回监听的服务器地址信息, 否则返回 QHostAddress::Null
QHostAddress QTcpServer::serverAddress() const;
// 如果服务器正在侦听连接,则返回服务器的端口; 否则返回0
quint16 QTcpServer::serverPort() const
参数:
address:通过类QHostAddress可以封装IPv4、IPv6格式的IP地址,QHostAddress::Any表示自动绑定
port:如果指定为0表示随机绑定一个可用端口。
返回值:绑定成功返回true,失败返回false
得到和客户端建立连接之后用于通信的QTcpSocket套接字对象,它是QTcpServer的一个子对象,当QTcpServer对象析构的时候会自动析构这个子对象,当然也可自己手动析构,建议用完之后自己手动析构这个通信的QTcpSocket对象。
QTcpSocket *QTcpServer::nextPendingConnection();
如果了解Linux网络编程的话就会理解,其实这就是相当于accept函数,返回一个socket
1.2信号
当接受新连接导致错误时,将发射如下信号。socketError参数描述了发生的错误相关的信息。
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);
每次有新连接可用时都会发出 newConnection() 信号。
[signal] void QTcpServer::newConnection();
2.QTcpSocket
QTcpSocket是一个套接字通信类,不管是客户端还是服务器端都需要使用。在Qt中发送和接收数据也属于IO操作(网络IO)。
2.1
构造函数
QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);
连接服务器,需要指定服务器端绑定的IP和端口信息。
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
[virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);
在Qt中不管调用读操作函数接收数据,还是调用写函数发送数据,操作的对象都是本地的由Qt框架维护的一块内存。因此,调用了发送函数数据不一定会马上被发送到网络中,调用了接收函数也不是直接从网络中接收数据,关于底层的相关操作是不需要使用者来维护的。这其实是与Linux类似的,也是维护一片缓冲区内存用于通信。
接收数据
// 指定可接收的最大字节数 maxSize 的数据到指针 data 指向的内存中
qint64 QIODevice::read(char *data, qint64 maxSize);
// 指定可接收的最大字节数 maxSize,返回接收的字符串
QByteArray QIODevice::read(qint64 maxSize);
// 将当前可用操作数据全部读出,通过返回值返回读出的字符串
QByteArray QIODevice::readAll();
发送数据
// 发送指针 data 指向的内存中的 maxSize 个字节的数据
qint64 QIODevice::write(const char *data, qint64 maxSize);
// 发送指针 data 指向的内存中的数据,字符串以 \0 作为结束标记
qint64 QIODevice::write(const char *data);
// 发送参数指定的字符串
qint64 QIODevice::write(const QByteArray &byteArray);
2.2信号
在使用QTcpSocket进行套接字通信的过程中,如果该类对象发射出readyRead()信号,说明对端发送的数据达到了,之后就可以调用 read 函数接收数据了。
[signal] void QIODevice::readyRead();
调用connectToHost()函数并成功建立连接之后发出connected()信号。
[signal] void QAbstractSocket::connected();
在套接字断开连接时发出disconnected()信号。
[signal] void QAbstractSocket::disconnected();
3.通信流程
3.1服务器端
1.创建套接字服务器QTcpServer对象
2.通过QTcpServer对象设置监听,即:QTcpServer::listen()
3.基于QTcpServer::newConnection()信号检测是否有新的客户端连接
4.如果有新的客户端连接调用QTcpSocket *QTcpServer::nextPendingConnection()得到通信的套接字对象
5.使用通信的套接字对象QTcpSocket和客户端进行通信
3.1.2代码片段
服务器端如图

头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
#include <QLabel>
#include <QPixmap>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_setlisten_clicked();
void on_sendmsg_clicked();
private:
Ui::MainWindow *ui;
QTcpServer *m_s;
QTcpSocket *m_tcp;
QLabel *m_status;
};
#endif // MAINWINDOW_H
源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port->setText("9527");
//实例化
m_s = new QTcpServer(this);
connect(m_s,&QTcpServer::newConnection,this,[=](){
m_tcp = m_s->nextPendingConnection();
//修改图片
m_status->setPixmap(QPixmap(":/OIP-C.jpg").scaled(20,20));
//检测
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray data = m_tcp->readAll();
ui->record->append("客户端发来:"+data);
});
//检测是否断开连接
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
m_tcp->close();
m_tcp->deleteLater(); //封装后的delete
m_status->setPixmap(QPixmap(":/th.jpg").scaled(20,20));
});
});
//状态栏
m_status = new QLabel;
m_status->setPixmap(QPixmap(":/th.jpg").scaled(20,20));
ui->statusBar->addWidget(new QLabel("连接状态"));
ui->statusBar->addWidget(m_status);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_setlisten_clicked()
{
unsigned short port = ui->port->text().toUShort();
//监听
m_s->listen(QHostAddress::Any,port);
//设置按钮不可选
ui->setlisten->setDisabled(true);
}
void MainWindow::on_sendmsg_clicked()
{
QString msg = ui->msg->toPlainText();
m_tcp->write(msg.toUtf8());
ui->record->append("服务端说: "+msg);
}
3.2客户端
客户端与服务器端其实差不多,稍微修改即可如图

3.2.1流程
1.创建通信的套接字类QTcpSocket对象
2.使用服务器端绑定的IP和端口连接服务器QAbstractSocket::connectToHost()
3.使用QTcpSocket对象和服务器进行通信
3.2.2代码
头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
#include <QLabel>
#include <QPixmap>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_sendmsg_clicked();
void on_connect_clicked();
void on_disconnect_clicked();
private:
Ui::MainWindow *ui;
QTcpSocket *m_tcp;
QLabel *m_status;
};
#endif // MAINWINDOW_H
源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostAddress>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->port->setText("9527");
ui->ip->setText("127.0.0.1");
setWindowTitle("客户端");
ui->disconnect->setDisabled(true);
//实例化
m_tcp = new QTcpSocket(this);
connect(m_tcp,&QTcpSocket::readyRead,this,[=](){
QByteArray data = m_tcp->readAll();
ui->record->append("服务端发来:"+data);
});
connect(m_tcp,&QTcpSocket::disconnected,this,[=](){
ui->connect->setDisabled(false);
ui->disconnect->setEnabled(false);
m_tcp->close();
//m_tcp->deleteLater(); //封装后的delete
m_status->setPixmap(QPixmap(":/th.jpg").scaled(20,20));
ui->record->append("连接断开.....");
});
connect(m_tcp,&QTcpSocket::connected,this,[=](){
ui->connect->setDisabled(true);
ui->disconnect->setEnabled(true);
m_status->setPixmap(QPixmap(":/OIP-C.jpg").scaled(20,20));
ui->record->append("连接成功.....");
});
//状态栏
m_status = new QLabel;
m_status->setPixmap(QPixmap(":/th.jpg").scaled(20,20));
ui->statusBar->addWidget(new QLabel("连接状态"));
ui->statusBar->addWidget(m_status);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_sendmsg_clicked()
{
QString msg = ui->msg->toPlainText();
m_tcp->write(msg.toUtf8());
ui->record->append("客户端说: "+msg);
}
void MainWindow::on_connect_clicked()
{
QString ip = ui->ip->text();
unsigned short port = ui->port->text().toUShort();
m_tcp->connectToHost(QHostAddress(ip),port);
}
void MainWindow::on_disconnect_clicked()
{
m_tcp->close();
ui->connect->setDisabled(true);
ui->disconnect->setEnabled(false);
}
下面是运行图片


浙公网安备 33010602011771号