文章目录
1.TCP通信
TCP通信过程
tcp通信必须先建立tcp连接,通信端分为客户端和服务器端。服务器端程序必须使用qtcpserver用于端口监听,建立服务器,qtcpsocket用于建立连接后使用套接字(socket)进行通信。
TCP CLIENT:QTcpSocket
TCP SERVER:QTcpServer,QTcpSocket
服务器
新建项目名为Tcp
ui界面完成一下布局,并选择上面的textedit,勾选右下角readonly
Tcp.pro添加network,并点击小锤子编译一下
QT += network
serverwidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include<QTcpServer> //监听套接字
#include<QTcpSocket> //通信套接字
QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE
class ServerWidget : public QWidget
{
Q_OBJECT
public:
ServerWidget(QWidget *parent = nullptr);
~ServerWidget();
private:
Ui::ServerWidget *ui;
QTcpServer *tcpServer; //监听套接字
QTcpSocket *tcpSocket; //通信套接字
};
#endif // SERVERWIDGET_H
serverwidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
ServerWidget::ServerWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ServerWidget)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
//监听套接字 指定父对象让其自动回收空间
tcpServer = new QTcpServer(this);
//监听当前网卡的所有ip地址,端口号8888
tcpServer->listen(QHostAddress::Any,8888);
setWindowTitle("服务器:8888");
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的ip和端口
QString ip = tcpSocket->peerAddress().toString();
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);
}
);
}
);
}
ServerWidget::~ServerWidget()
{
delete ui;
}
void ServerWidget::on_ButtonSend_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//获取编辑区的内容
QString str = ui->textEditWrite->toPlainText();
//给对方发送数据,使用TCPsocket套接字
tcpSocket -> write(str.toUtf8().data());
}
void ServerWidget::on_ButtonClose_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//主动和客户端断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
tcpSocket = NULL;
}
客户端
项目右键,添加文件,选择QT设计师界面类
选择widget
设计ui界面,上面的texteidt选择readonly
clientwidget.h
#include<QTcpSocket>
private:
Ui::ClientWidget *ui;
QTcpSocket *tcpSocket; //指针
};
main.cpp
#include "serverwidget.h"
#include <QApplication>
#include"clientwidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerWidget w;
w.show();
ClientWidget w2;
w2.show();
return a.exec();
}
clientwidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
ServerWidget::ServerWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ServerWidget)
{
ui->setupUi(this);
tcpServer = NULL;
tcpSocket = NULL;
//监听套接字 指定父对象让其自动回收空间
tcpServer = new QTcpServer(this);
//监听当前网卡的所有ip地址,端口号8888
tcpServer->listen(QHostAddress::Any,8888);
setWindowTitle("服务器:8888");
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的ip和端口
QString ip = tcpSocket->peerAddress().toString();
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);
}
);
}
);
}
ServerWidget::~ServerWidget()
{
delete ui;
}
void ServerWidget::on_ButtonSend_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//获取编辑区的内容
QString str = ui->textEditWrite->toPlainText();
//给对方发送数据,使用TCPsocket套接字
tcpSocket -> write(str.toUtf8().data());
}
void ServerWidget::on_ButtonClose_clicked()
{
if(NULL == tcpSocket)
{
return;
}
//主动和客户端断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
tcpSocket = NULL;
}
运行结果
点击客户端connect,客户端和服务器显示连接成功,可以通信
客户端输入hello whisper,点击send按钮,服务器接收
服务器输入hi evil,点击send按钮,客户端接收
点击close,断开连接
2.UDP通信
UDP通信过程
与tcp通信不同,两个程序之间进行udp通信无需预先建立持久的socket连接,udp每次发送数据报都需要指定目标地址和端口。
UDP
Udp.pro添加network,并点击小锤子编译一下
QT += network
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QUdpSocket>//udp套接字
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void dealMsg(); //槽函数,处理对方发过来的数据
private:
Ui::Widget *ui;
QUdpSocket *udpSocket; //指针
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("服务器端口:8888");
//分配空间,指定父对象
udpSocket = new QUdpSocket(this);
//绑定
udpSocket->bind(8888);
//当对方成功发送数据过来,自动触发readyRead()
//这里使用槽函数的方式
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
}
void Widget::dealMsg()
{
//读取对方发送的内容
char buf[1024] = {0};
QHostAddress cliAddr;//对方地址
quint16 port; //对方端口
qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
if (len>0)
{
//格式化 [192.68.2.2:8888]aaaa
QString str = QString("[%1:%2] %3")
.arg(cliAddr.toString())
.arg(port)
.arg(buf);
//给编辑区设置内容
ui->textEdit->setText(str);
}
}
Widget::~Widget()
{
delete ui;
}
//发送按钮
void Widget::on_buttonSend_clicked()
{
//获取对方的IP和端口
QString ip = ui->lineEditIP->text();
qint16 port = ui->lineEditPort->text().toInt();
//获取编辑区内容
QString str = ui->textEdit->toPlainText();
//给指定的IP发送数据
udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
}
运行结果
输入对方的IP和端口即可发送信息
UDP组播
//绑定的地方要指定ipv4
udpSocket->bind(QHostAddress::AnyIPv4,8888);
//加入某个组播
//组播地址是D类地址
udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
//udpSocket->leaveMulticastGroup(); //退出组播
3.定时器
完成ui界面的设计
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTime> //定时器对象头文件
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QTimer *myTimer;//创建定时器指针
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QTimer>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
myTimer = new QTimer(this);
connect(myTimer,&QTimer::timeout,
[=]()
{
static int i;
i++;
ui->lcdNumber->display(i);
}
);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_buttonStart_clicked()
{
//启动定时器,时间间隔为100ms
//每隔100ms,定时器myTimer自动触发timeout()
//如果定时器不是活动的时候才启动
if(myTimer->isActive()==false)
{
myTimer->start(100);
}
}
void Widget::on_pushButton_2_clicked()
{
//如果定时器活动时,才停止
if(myTimer->isActive()==true)
{
myTimer->stop();
}
}
运行结果
点击start按钮,开始计时,点击end按钮,停止计时
4.TCP传文件
TCP流程图
服务器
TCP_file.pro
QT += core gui network
ui界面
serverwidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字
#include<QFile>
#include<QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACE
class ServerWidget : public QWidget
{
Q_OBJECT
public:
ServerWidget(QWidget *parent = nullptr);
~ServerWidget();
void sendData();//发送文件数据
private slots:
void on_buttonFile_clicked();
void on_buttonSend_clicked();
private:
Ui::ServerWidget *ui;
QTcpServer *tcpServer;
QTcpSocket *tcpSocket;
QFile file;//文件对象
QString fileName;//文件名字
qint64 fileSize;//文件大小
qint64 sendSize;//已经发送文件大小
QTimer timer;//定时器
};
#endif // SERVERWIDGET_H
serverwidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
#include<QFileDialog>
#include<QDebug>
#include<QFileInfo>
ServerWidget::ServerWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::ServerWidget)
{
ui->setupUi(this);
//监听套接字
tcpServer = new QTcpServer(this);
//监听
tcpServer->listen(QHostAddress::Any,8888);
setWindowTitle("服务器端口:8888");
//两个按钮都不能按
ui->buttonFile->setEnabled(false);
ui->buttonSend->setEnabled(false);
//如果客户端成功和服务器连接
//tcpServer会自动触发,newConeection()
connect(tcpServer,&QTcpServer::newConnection,
[=]()
{
//取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
//获取对方的IP和端口
QString ip = tcpSocket->peerAddress().toString();
quint16 port = tcpSocket->peerPort();
QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
ui->textEdit->setText(str);//显示到编辑区
//成功连接后,才能按选择文件
ui->buttonFile->setEnabled(true);
}
);
connect(&timer,&QTimer::timeout,
[=]()
{
//关闭定时器
timer.stop();
//发送文件
sendData();
}
);
}
ServerWidget::~ServerWidget()
{
delete ui;
}
//选择文件按钮
void ServerWidget::on_buttonFile_clicked()
{
QString filepath = QFileDialog::getOpenFileName(this,"open","../");
if(filepath.isEmpty() == false)//如果选择文件路径有效
{
fileName.clear();
fileSize = 0;
//获取文件信息
QFileInfo info(filepath);
fileName = info.fileName();//获取文件名字
fileSize = info.size();//获取文件大小
sendSize = 0;//发送文件的大小
//只读方式打开文件
//指定文件的名字
file.setFileName(filepath);
//打开文件
bool isOk = file.open(QIODevice::ReadOnly);
if(isOk==false)
{
qDebug() << "只读方式发开文件失败";
}
//提示打开文件的路径
ui->textEdit->append(filepath);
ui->buttonFile->setEnabled(false);
ui->buttonSend->setEnabled(true);
}
else
{
qDebug() << "选择文件路径出错";
}
}
//发送文件按钮
void ServerWidget::on_buttonSend_clicked()
{
//先发送文件头信息 文件名##文件大小
QString head = QString("%1##%2").arg(fileName).arg(fileSize);
//发送头部信息
qint64 len = tcpSocket->write(head.toUtf8());
if(len>0)//头部信息发送成功
{
//发送真正的文件信息
//防止tcp粘包问题
//需要通过定时器延时 20ms
timer.start(20);
}
else
{
qDebug() << "头部信息发送失败";
file.close();
ui->buttonFile->setEnabled(true);
ui->buttonSend->setEnabled(false);
}
}
void ServerWidget::sendData()
{
qint64 len = 0;
do
{
//每次发送文件的大小
char buf[4*1024] = {0};
len = 0;
//往文件中读数据
len = file.read(buf,sizeof(buf));
//发送数据,读多少,发多少
len = tcpSocket->write(buf,len);
//发送的数据需要累积
sendSize += len;
}while(len>0);
//是否发送文件完毕
if(sendSize == fileSize)
{
ui->textEdit->append("文件发送完毕");
file.close();
//把客户端端口
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
客户端
新建文件,QT->设计师界面
main.cpp
#include "serverwidget.h"
#include <QApplication>
#include"clientwidget.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerWidget w;
w.show();
ClientWidget w2;
w2.show();
return a.exec();
}
clientwidget.h
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include<QTcpSocket>
#include<QFile>
namespace Ui {
class ClientWidget;
}
class ClientWidget : public QWidget
{
Q_OBJECT
public:
explicit ClientWidget(QWidget *parent = nullptr);
~ClientWidget();
private slots:
void on_pushButton_clicked();
private:
Ui::ClientWidget *ui;
QTcpSocket *tcpSocket;
QFile file;//文件对象
QString fileName;//文件名字
qint64 fileSize;//文件大小
qint64 recvSize;//已经接收文件大小
bool isStart;
};
#endif // CLIENTWIDGET_H
clientwidget.cpp
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include<QDebug>
#include<QMessageBox>
#include<QHostAddress>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
setWindowTitle("客户端");
tcpSocket = new QTcpSocket(this);
isStart = true;
connect(tcpSocket,&QTcpSocket::readyRead,
[=]()
{
//取出接收的内容
QByteArray buf = tcpSocket->readAll();
if(true == isStart)
{//接收头
isStart = false;
//解析头部信息 QString buf ="hello##1024"
//字符串拆包
//QString str = "hello##1024";
//str.section("##",0,0);//从第0段开始到第0段结束,##隔开,hello是第0段,1024是第一段
//初始化
fileName = QString(buf).section("##",0,0);
fileSize = QString(buf).section("##",1,1).toInt();
recvSize = 0;
//打开文件
file.setFileName(fileName);
bool isOk = file.open(QIODevice::WriteOnly);
if(isOk == false)
{
qDebug() << "writeonly error";
}
}
else//文件信息
{
qint64 len = file.write(buf);
recvSize += len;
if(recvSize == fileSize)
{
file.close();
QMessageBox::information(this,"完成","文件接收完成");
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
}
);
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_pushButton_clicked()
{
//获取服务器的IP和端口
QString ip = ui->lineEditIP->text();
quint16 port = ui->lineEditPort->text().toInt();
tcpSocket->connectToHost(QHostAddress(ip),port);
}
运行结果
显示客户端和服务器界面,此时选择文件和发送文件按钮都无法选择
点击客户端的connect,连接客户端和服务器,连接成功后选择文件按钮可以选择
选择想要发送的文件并点击发送文件
文件发送成功
在build文件夹下可以找到发送的文件
5.Thread 线程
简单示例
ui界面
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void dealTimeout(); //定时器槽函数,处理定时器信号
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
QTimer *myTimer; //声明指针变量
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QThread> //线程
#include<QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
myTimer = new QTimer(this);
//只要定时器启动,自动触发timeout
connect(myTimer,&QTimer::timeout,this,&Widget::dealTimeout);
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealTimeout()
{
static int i = 0;
i++;
//设置lcd的值
ui->lcdNumber->display(i);
}
void Widget::on_pushButton_clicked()
{
//如果定时器没有工作
if(myTimer->isActive()==false)
{
myTimer->start(100);
}
//非常复杂的数据处理,耗时较长
QThread::sleep(5);
//处理完数据,关闭定时器
myTimer->stop();
qDebug() << "over";
}
运行结果:点击start按钮,界面没有反应,5秒后关闭定时器并打印“over”。把myTimer->stop();注释掉,则5秒后定时器开始运行。
线程
新建文件,选择C++,基类选择QObject