Qt中使用Tcp構建通信客戶端實現聊天信息發送連接等
1.簡介
TCP通信協議是面向對象,可靠的連接服務,正因爲這是它最大的特點,因此在諸多領域應用廣泛,在使用Qt進行面向對象的設計的時候,也常常用到TCP通信,而在Qt中,TCP協議被封裝成了一個更易調用的類。此篇博文將使用Qt中的TCP通信來實現客戶端和服務端的連接交互通信,以此作爲延伸。
在之前的一篇博文中 ,Linux中C語言構建TCP通信,使用了C語言對TCP協議進行調用,來實現客戶端和服務端的通信連接。而Qt中的TCP通信連接步驟和C語言中原生tcp連接的步驟一樣,大體都是分爲以下大類
- 創建套接字
- 綁定本機地址和端口
- 設置監聽套接字
- 主動向服務器(客戶端)發送請求
- 客戶端(服務器)接受連接請求
- 接受/發送數據
- (雙方)關閉套接字
區別在於,Qt中調用TCP的方式簡單,可以實現如下的效果
以下是詳細的實際實現
2.項目創建和界面構建
1)流程圖
TCP協議通信邏輯圖有下圖,根據此邏輯圖,可以更好的理解Qt中的TCP協議的調用
2)項目構建
創建widget項目,除自有界面外,再添加一個新的界面,構建如下圖所示的項目文件樹
3)界面構建
客戶端界面構建
服務端界面構建
兩個界面大體是構建上面是顯示消息欄。下面是輸入消息發送欄。
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;
}
詳細效果,可戳傳送門下載體驗傳送門