使用Qt中的tcp通信协议,构建客户端和服务端,实现聊天功能
程序员文章站
2022-05-19 13:40:49
...
Qt中使用Tcp构建通信聊天信息发送连接
1.简介
tcp通信协议是面向对象,可靠的连接服务,正因为这是它最大的特点,因此在诸多领域应用广泛,在使用Qt进行面向对象的设计的时候,也常常用到tcp通信,而在Qt中,tcp被封装成了一个更易调用的类。此篇博文将使用Qt中的tcp通信来实现客户端和服务端的连接交互通信,以此作为延伸。
在之前的一篇博文中 ,Linux中C语言构建tcp通信,使用了C语言对tcp协议进行调用,来实现客户端和服务端的通信连接。而Qt中的tcp通信连接步骤和C语言中原生tcp连接的步骤一样,大体都是分为以下大类
- 创建套接字
- 绑定本机地址和端口
- 设置监听套接字
- 主动向服务器(客户端)发送请求
- 客户端(服务器)接受连接请求
- 接受/发送数据
- (双方)关闭套接字
区别在于,Qt中调用tcp的方式简单,可以实现如下的效果
以下是详细的实际实现
2.项目创建和界面构建
1)项目构建
创建widget项目,除自有界面外,再添加一个新的界面,构建如下图所示的项目文件树
2)界面构建
客户端界面构建
服务端界面构建
两个界面大体是构建上面是显示消息栏。下面是输入消息发送栏。
3.代码设计
1)项目pro添加
第一步需要在项目的pro文件中添加网络通信模块
QT += network
2)客户端设计
a. clientwidget.h
首先需要添加所需头文件,用于tcp通信
#include <QTcpSocket>
之后是tcp通信的类的声明以及一个相应键盘事件的事件过滤器
private:
QTcpSocket *tcpSocket;
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
};
b.clientwdige.cpp
同样是添加所需工具头文件
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>
之后是构造函数和部分相关函数的设计
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
this->resize(500,300);
this->setMinimumSize(500,300);
tcpSocket = new QTcpSocket(this);
connect(tcpSocket,&QTcpSocket::connected,
[=]()
{
ui->textEditRead->setText("与服务器成功连接");
qDebug() << "连接成功";
}
);
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array);
}
);
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditWrite->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_ButtonConnect_clicked()
{
//获取服务器端口和ip
QString ip = ui->lineEditIp->text();
qint16 port = ui->lineEditPort->text().toInt();
//与服务器进行连接
tcpSocket->connectToHost(QHostAddress(ip),port);
}
void ClientWidget::on_ButtonSend_clicked()
{
//获取编辑框内容
QTime cur_time = QTime::currentTime();
QString str = ui->textEditWrite->toPlainText();
QString time_info = cur_time.toString("hh:mm:ss");
QString str_info = QString("客户端: %1 [%2]").arg(str).arg(time_info);
//发送数据
tcpSocket->write(str_info.toUtf8().data());
ui->textEditWrite->clear();
//ui->textEditWrite->setFocus();
}
void ClientWidget::on_ButtonClose_clicked()
{
//主动断开连接
tcpSocket->disconnectFromHost();
ui->textEditRead->setText("与服务器断开连接!");
tcpSocket->close();
}
3)服务端设计
a.serverwidget.h
添加头文件
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
通信套接字声明实现和事件过滤器
private:
QTcpServer *tcpServer;
QTcpSocket *tcpSocket;
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
b.serverwidget.cpp
添加工具头文件
#include <QTime>
#include <QKeyEvent>
构造函数设计
ServerWidget::ServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ServerWidget)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
//
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
tcpServer->listen(QHostAddress::Any,8888);
this->setWindowTitle("服务器(端口:8888)");
this->resize(500,300);
this->setMinimumSize(500,300);
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的ip和端口s
static QString ip = tcpSocket->peerAddress().toString();
static qint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2] : 成功连接").arg(ip).arg(port);
ui->textEditRead->setText(temp);
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
//从通信套接字中取出内容
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array); //追加添加内容
}
);
}
);
// connect(tcpSocket,&QTcpSocket::readyRead,
// [=]()
// {
// //从通信套接字中取出内容
// QByteArray array = tcpSocket->readAll();
// ui->textEditRead->append(array); //追加添加内容
// }
// );
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditWrite->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
经此设计构建之后可以实现如上的功能操作,有此基础之后可以自定定义自己的服务器,如智能小车的控制,摄像头图像的传递识别等等
4.源代码附录
clientwidget.h
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include <QTcpSocket>
namespace Ui {
class ClientWidget;
}
class ClientWidget : public QWidget
{
Q_OBJECT
public:
explicit ClientWidget(QWidget *parent = nullptr);
~ClientWidget();
private slots:
void on_ButtonSend_clicked();
void on_ButtonConnect_clicked();
void on_ButtonClose_clicked();
private:
Ui::ClientWidget *ui;
private:
QTcpSocket *tcpSocket;
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
};
#endif // CLIENTWIDGET_H
clientwidget.cpp
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
this->resize(500,300);
this->setMinimumSize(500,300);
tcpSocket = new QTcpSocket(this);
connect(tcpSocket,&QTcpSocket::connected,
[=]()
{
ui->textEditRead->setText("与服务器成功连接");
qDebug() << "连接成功";
}
);
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array);
}
);
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditWrite->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_ButtonConnect_clicked()
{
//获取服务器端口和ip
QString ip = ui->lineEditIp->text();
qint16 port = ui->lineEditPort->text().toInt();
//与服务器进行连接
tcpSocket->connectToHost(QHostAddress(ip),port);
}
void ClientWidget::on_ButtonSend_clicked()
{
//获取编辑框内容
QTime cur_time = QTime::currentTime();
QString str = ui->textEditWrite->toPlainText();
QString time_info = cur_time.toString("hh:mm:ss");
QString str_info = QString("客户端: %1 [%2]").arg(str).arg(time_info);
//发送数据
tcpSocket->write(str_info.toUtf8().data());
ui->textEditWrite->clear();
//ui->textEditWrite->setFocus();
}
void ClientWidget::on_ButtonClose_clicked()
{
//主动断开连接
tcpSocket->disconnectFromHost();
ui->textEditRead->setText("与服务器断开连接!");
tcpSocket->close();
}
bool ClientWidget::eventFilter(QObject *target, QEvent *event)
{
if(target == ui->textEditWrite)
{
if(event->type() == QEvent::KeyPress)//回车键
{
QKeyEvent *k = static_cast<QKeyEvent *>(event);
if(k->key() == Qt::Key_Return)
{
on_ButtonSend_clicked();
return true;
}
}
}
return QWidget::eventFilter(target,event);
}
serverwidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
namespace Ui {
class ServerWidget;
}
class ServerWidget : public QWidget
{
Q_OBJECT
public:
explicit ServerWidget(QWidget *parent = nullptr);
~ServerWidget();
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private slots:
void on_ButtonClose_clicked();
void on_ButtonSend_clicked();
private:
Ui::ServerWidget *ui;
private:
QTcpServer *tcpServer;
QTcpSocket *tcpSocket;
// QString ip;
// qint16 port;
};
#endif // SERVERWIDGET_H
serverwidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QTime>
#include <QKeyEvent>
ServerWidget::ServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ServerWidget)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
//
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
tcpServer->listen(QHostAddress::Any,8888);
this->setWindowTitle("服务器(端口:8888)");
this->resize(500,300);
this->setMinimumSize(500,300);
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的ip和端口s
static QString ip = tcpSocket->peerAddress().toString();
static qint16 port = tcpSocket->peerPort();
QString temp = QString("[%1:%2] : 成功连接").arg(ip).arg(port);
ui->textEditRead->setText(temp);
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
//从通信套接字中取出内容
QByteArray array = tcpSocket->readAll();
ui->textEditRead->append(array); //追加添加内容
}
);
}
);
// connect(tcpSocket,&QTcpSocket::readyRead,
// [=]()
// {
// //从通信套接字中取出内容
// QByteArray array = tcpSocket->readAll();
// ui->textEditRead->append(array); //追加添加内容
// }
// );
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditWrite->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
ServerWidget::~ServerWidget()
{
delete ui;
}
bool ServerWidget::eventFilter(QObject *target, QEvent *event)
{
if(target == ui->textEditWrite)
{
if(event->type() == QEvent::KeyPress)//回车键
{
QKeyEvent *k = static_cast<QKeyEvent *>(event);
if(k->key() == Qt::Key_Return)
{
on_ButtonSend_clicked();
return true;
}
}
}
return QWidget::eventFilter(target,event);
}
void ServerWidget::on_ButtonSend_clicked()
{
if(tcpSocket == NULL)
{
return ;
}
//获取编辑区内容
QTime cur_time = QTime::currentTime();
QString str = ui->textEditWrite->toPlainText();
QString time_info = cur_time.toString("hh:mm:ss");
QString str_info = QString("服务器: %1 [%2]").arg(str).arg(time_info);
tcpSocket->write(str_info.toUtf8().data());
ui->textEditWrite->clear();
}
void ServerWidget::on_ButtonClose_clicked()
{
if(tcpSocket == NULL)
//主动与客户端断开连接
tcpSocket->disconnectFromHost();
static QString ip = tcpSocket->peerAddress().toString();
static qint16 port = tcpSocket->peerPort();
QString showtemp = QString("[%1:%2] : 断开连接").arg(ip).arg(port);
ui->textEditRead->setText(showtemp);
tcpSocket->close();
//this->close();
tcpSocket = NULL;
}
详细效果,可戳传送门下载体验传送门