1、前言
在标准C++中没有提供专门用于套接字通信的类,所以只能使用操作系统提供的基于C语言的API函数,基于这些C的API函数我们也可以封装自己的C++类。或者我们可以使用Qt框架,它提供了用于套接字通信的类(TCP、UDP)这样我们就可以直接调用相关API即可。
使用Qt提供的类进行基于TCP的套接字通信需要使用两个类:
- QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接。
- QTcpSocket:通信套接字类,客户端与服务器都需要使用这个类。
最后要使用这两个类,我们需要在Qt的.pro文件中添加network(网络模块)。
2、QTcpServer
QTcpServer类用于监听客户端的连接以及和客户端建立连接,下面介绍一下这个类中常用的API:
2.1、成员函数
构造函数如下:
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);
监听是否有客户端连接的API:
//监听客户端的Ip地址以及端口
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表示任意IP地址。
- port:指定的端口号。
每当有客户端成功连接服务器时,我们会通过下面这个API获取用于和客户端通信的套接字:
QTcpSocket *QTcpServer::nextPendingConnection();
2.2、信号
当接受新连接导致错误时,将会发出如下信号:
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);
每当有客户端连接时会发出如下信号:
[signal] void QTcpServer::newConnection();
3.1、QTcpSocket
QTcpSocket是一个套接字通信类,不管是客户端还是服务器都需要使用。在Qt中发送和接收数据也属于IO操作。
3.1、成员函数
QTcpSocket的构造函数如下:
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框架维护的一块内存。因此,调用发送函数发送的数据不一定马上被发送到网络中,调用的接收函数也不是直接从网络中接收的数据。
接收数据的API:
// 指定可接收的最大字节数 maxSize 的数据到指针 data 指向的内存中
qint64 QIODevice::read(char *data, qint64 maxSize);
// 指定可接收的最大字节数 maxSize,返回接收的字符串
QByteArray QIODevice::read(qint64 maxSize);
// 将当前可用操作数据全部读出,通过返回值返回读出的字符串
QByteArray QIODevice::readAll();
发送数据的API:
// 发送指针 data 指向的内存中的 maxSize 个字节的数据
qint64 QIODevice::write(const char *data, qint64 maxSize);
// 发送指针 data 指向的内存中的数据,字符串以 \0 作为结束标记
qint64 QIODevice::write(const char *data);
// 发送参数指定的字符串
qint64 QIODevice::write(const QByteArray &byteArray);
3.2、信号
在使用QTcpSocket进行套接字通信的过程中,如果该类对象发射出了readyRead()信号,说明对端发送的数据达到了,之后可以调用read函数接收数据了。
[signal] void QIODevice::readyRead();
调用connectToHost()函数并成功建立连接之后发出connected()信号。
[signal] void QAbstractSocket::connected();
套接字与服务器断开连接发出disconnected信号。
[signal] void QAbstractSocket::disconnected();
4、TCP通信流程-实现简单的TCP聊天
4.1、服务器端通信流程
- 创建服务器对象QTcpServer。
- 通过QTcpServer对象设置监听,即使用listen()。
- 使用newConnection()信号判断是否有新的客户端建立连接。
- 若有新的客户端连接,则调用nextPendingConnection()得到通信的套接字对象。
- 使用QTcpSocket套接字对象与客户端进行通信。
4.2、服务器端UI
上面的按钮和文本编辑框采用Material UI库的组件,直接导入Material组件库,然后再将控件提升即可,QGroupBox则是用qss进行简单的美化。如下是美化QGroupBox的qss代码:
QGroupBox {
border:3px solid rgb(53, 153, 252);
margin-top: 2.5ex;
}
QGroupBox::title {
color: rgb(0, 0, 0);
subcontrol-origin: margin;
subcontrol-position: top left;
left:10px;
top:0px;
padding: 0px;
}
QGroupBox::indicator {
width: 15px;
height: 15px;
}
4.3、导入Material组件库并提升控件
首先在项目文件中创建SDK的文件目录:
然后将在Material组件库中编译好静态库文件和所有头文件导入SDK目录中。
然后再服务器的pro文件中导入Material的静态库和头文件:
最后将控件提升即可。
4.4、服务器端源码
服务器端的源码如下:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QTcpServer>
#include<QTcpSocket>
#include<QPalette>
#include<QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_startServer_btn_clicked();
void on_send_btn_clicked();
void on_clear_btn_clicked();
private:
Ui::MainWindow *ui;
QTcpServer *m_tcpServer;
QTcpSocket *m_tcpSocket;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("Tcp聊天-服务器");
// 默认断开连接
this->ui->statusbar->setStyleSheet(QString("color:red;"));
this->ui->statusbar->showMessage("连接状态:断开");
//创建服务器
m_tcpServer = new QTcpServer(this);
//当有客户端连接时
connect(m_tcpServer,&QTcpServer::newConnection,this,[=](){
m_tcpSocket = m_tcpServer->nextPendingConnection(); //获取与客户端通信的套接字
//更新连接状态
this->ui->statusbar->setStyleSheet(QString("color:green;"));
this->ui->statusbar->showMessage("连接状态:连接");
//当客户端发来数据时
connect(m_tcpSocket,&QTcpSocket::readyRead,this,[=](){
QByteArray data = m_tcpSocket->readAll();
this->ui->record->append("客户端:"+data);
});
//客户端断开连接
connect(m_tcpSocket,&QTcpSocket::disconnected,this,[=](){
m_tcpSocket->close();
m_tcpSocket->deleteLater();
this->ui->statusbar->setStyleSheet(QString("color:red;"));
this->ui->statusbar->showMessage("连接状态:断开");
});
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_startServer_btn_clicked()
{
unsigned short port = this->ui->port->text().toInt();
//服务器监听指定的ip地址和端口
m_tcpServer->listen(QHostAddress::Any,port);
this->ui->startServer_btn->setEnabled(false);
}
void MainWindow::on_send_btn_clicked()
{
//设置发送按钮的快捷键
this->ui->send_btn->setShortcut(Qt::Key_Enter | Qt::Key_Return);
//向客户端发送消息
QString msg = this->ui->send->toPlainText();
m_tcpSocket->write(msg.toUtf8());
this->ui->record->append("服务器:"+msg);
this->ui->send->clear();
}
void MainWindow::on_clear_btn_clicked()
{
this->ui->record->clear();
}
4.5、客户端通信流程
- 创建通信的套接字类QTcpSocket对象。
- 使用connectToHost()绑定服务器的IP和端口。
- 使用QTcpSocket对象和服务器通信。
4.6、客户端UI
4.7、客户端源码
客户端源码如下:文章来源:https://www.toymoban.com/news/detail-854835.html
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QTcpSocket>
#include<QDebug>
#include<QMessageBox>
#include<QHostAddress>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_conServer_btn_clicked();
void on_dicon_btn_clicked();
void on_clear_clicked();
void on_send_btn_clicked();
private:
Ui::MainWindow *ui;
QTcpSocket *m_tcpSocket;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->setWindowTitle("Tcp聊天-客户端");
m_tcpSocket = new QTcpSocket(this);
// 默认断开连接
this->ui->statusbar->setStyleSheet(QString("color:red;"));
this->ui->statusbar->showMessage("连接状态:断开");
//设置ip和port
this->ui->ip->setText("127.0.0.1");
this->ui->port->setText("8888");
this->ui->dicon_btn->setEnabled(false);
//客户端接收到服务器传输的数据
connect(m_tcpSocket,&QTcpSocket::readyRead,this,[=](){
QByteArray data = m_tcpSocket->readAll();
this->ui->record->append("服务器:"+data);
});
//客户端连接服务器成功
connect(m_tcpSocket,&QTcpSocket::connected,this,[=](){
//更新连接状态
this->ui->statusbar->setStyleSheet(QString("color:green;"));
this->ui->statusbar->showMessage("连接状态:连接");
QMessageBox::information(this,"信息","连接服务器成功!!");
this->ui->record->append("连接服务器成功......");
this->ui->conServer_btn->setEnabled(false);
this->ui->dicon_btn->setEnabled(true);
});
//客户端断开连接
connect(m_tcpSocket,&QTcpSocket::disconnected,this,[=](){
// 默认断开连接
this->ui->statusbar->setStyleSheet(QString("color:red;"));
this->ui->statusbar->showMessage("连接状态:断开");
// //释放资源
// m_tcpSocket->close();
// m_tcpSocket->deleteLater();
//更改按钮状态
this->ui->conServer_btn->setEnabled(true);
this->ui->dicon_btn->setEnabled(false);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_conServer_btn_clicked()
{
QString ip = this->ui->ip->text();
unsigned short port = this->ui->port->text().toInt();
this->ui->conServer_btn->setEnabled(false);
//连接服务器
m_tcpSocket->connectToHost(QHostAddress(ip),port);
}
void MainWindow::on_dicon_btn_clicked()
{
m_tcpSocket->close();
//m_tcpSocket->deleteLater();
this->ui->conServer_btn->setEnabled(true);
this->ui->dicon_btn->setEnabled(false);
}
void MainWindow::on_clear_clicked()
{
this->ui->record->clear();
}
void MainWindow::on_send_btn_clicked()
{
this->ui->send_btn->setShortcut(Qt::Key_Enter | Qt::Key_Return);
QString msg = this->ui->send->toPlainText();
m_tcpSocket->write(msg.toUtf8());
this->ui->record->append("客户端:"+msg);
this->ui->send->clear();
}
5、效果图
文章来源地址https://www.toymoban.com/news/detail-854835.html
到了这里,关于Qt实现TCP网络通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!