Qt学习第4天:TCP/UDP通信 【笔记】
发布日期:2021-07-01 04:00:41
浏览次数:2
分类:技术文章
本文共 14247 字,大约阅读时间需要 47 分钟。
文章目录
1.Linux下的TCP通信过程
2.Qt下的TCP通信过程
注意,服务器有两个套接字:QTcpServer
、QTcpSocket
,即监听套接字和通信套接字. 3. TCP通信
TCP服务器
TextEdit设置只读:
serverwidget.h#ifndef SERVERWIDGET_H#define SERVERWIDGET_H#include#include //监听套接字#include //通信套接字namespace Ui { class ServerWidget;}class ServerWidget : public QWidget{ Q_OBJECTpublic: explicit ServerWidget(QWidget *parent = nullptr); ~ServerWidget();private slots: void on_buttonSent_clicked(); void on_buttonClose_clicked();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; setWindowTitle("服务器:8888"); //监听套接字,指定父对象,让其自动回收空间 tcpServer = new QTcpServer(this); tcpServer->listen(QHostAddress::Any,8888);//绑定网卡所有IP,端口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_buttonSent_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;}
TCP客户端
clientwidget.h
#ifndef CLIENTWIDGET_H#define CLIENTWIDGET_H#include#include //通信套接字namespace Ui { class ClientWidget;}class ClientWidget : public QWidget{ Q_OBJECTpublic: explicit ClientWidget(QWidget *parent = nullptr); ~ClientWidget();private slots: void on_buttonConnect_clicked(); void on_buttonSend_clicked(); void on_buttonClose_clicked(); private: Ui::ClientWidget *ui; QTcpSocket *tcpSocket;};#endif // CLIENTWIDGET_H
clientwidget.cpp
#include "clientwidget.h"#include "ui_clientwidget.h"#includeClientWidget::ClientWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ClientWidget){ ui->setupUi(this); setWindowTitle("客户端"); tcpSocket = NULL; //分配空间,指定父对象 tcpSocket = new QTcpSocket(this); connect(tcpSocket,&QTcpSocket::connected, [=]() { ui->textEditRead->setText("成功和服务器建立连接!"); }); connect(tcpSocket,&QTcpSocket::readyRead, [=]() { //获取对方发送的内容 QByteArray array = tcpSocket->readAll(); //追加到编辑区 ui->textEditRead->append(array); } );}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(){ //获取编辑框内容 QString str = ui->textEditWrite->toPlainText(); //发送数据 tcpSocket->write(str.toUtf8().data());}void ClientWidget::on_buttonClose_clicked(){ //主动和对方断开连接 tcpSocket->disconnectFromHost(); tcpSocket->close();}
4. UDP通信
UDP通信过程
Linux下的UDP
Qt下的UDP
UDP与TCP的区别
- UDP像写信,只要知道地址就可以发
- TCP像打电话,只有两人同时在线才能通信
UDP 文本发送
widget.h
#ifndef WIDGET_H#define WIDGET_H#include#include //UDP套接字namespace Ui { class Widget;}class Widget : public QWidget{ Q_OBJECTpublic: explicit Widget(QWidget *parent = nullptr); ~Widget(); void dealMsg();//槽函数,处理对方发过来的数据private slots: void on_buttonSend_clicked();private: Ui::Widget *ui; QUdpSocket *udpSocket;//UDP套接字};#endif // WIDGET_H
widget.cpp
#include "widget.h"#include "ui_widget.h"#includeWidget::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);}
UDP多播组播
widget.cpp#include "widget.h"#include "ui_widget.h"#includeWidget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget){ ui->setupUi(this); setWindowTitle("服务器端口为:8888"); //分配空间,指定父对象 udpSocket = new QUdpSocket(this); //绑定 //udpSocket->bind(8888); //注意,群播时绑定的是IPv4 udpSocket->bind(QHostAddress::AnyIPv4,8888); //加入某个组播 //组播地址必须是D类地址 udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2")); //udpSocket->leaveMulticastGroup(); //退出组播 //当对方成功发送数据过来 //自动触发 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);}
5. QTimer定时器
5.TCP传文件
务必注意TCP的黏包问题,通常通过发送头部数据
+延时
来处理!!! main.cpp
#include "serverwidget.h"#include#include "clientwidget.h"int main(int argc, char *argv[]){ QApplication a(argc, argv); ServerWidget w; w.show(); ClientWidget w2; w2.show(); return a.exec();}
服务器
serverwidget.h
#ifndef SERVERWIDGET_H#define SERVERWIDGET_H#include#include //监听套接字#include //通信套接字#include #include namespace Ui { class ServerWidget;}class ServerWidget : public QWidget{ Q_OBJECTpublic: explicit 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#include #include 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); //如果客户端成功和服务器连接 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(false == filePath.isEmpty())//如果选择文件路径无效 { 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(false == isOk) { 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);//如果len≤0,发送完毕 //文件是否发送完毕 if(sendSize == fileSize) { ui->textEdit->append("文件发送完毕!"); file.close(); //把客户端端口关闭 tcpSocket->disconnectFromHost(); tcpSocket->close(); }}
客户端
clientwidget.h
#ifndef CLIENTWIDGET_H#define CLIENTWIDGET_H#include#include #include namespace Ui { class ClientWidget;}class ClientWidget : public QWidget{ Q_OBJECTpublic: explicit ClientWidget(QWidget *parent = nullptr); ~ClientWidget();private slots: void on_buttonConnect_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#include #include ClientWidget::ClientWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ClientWidget){ ui->setupUi(this); ui->progressBar->setValue(0);//设置进度条当前值为0 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#mike"; //取hello: str.section("##",0,0) //取1024:str.section("##",1,1).toInt() //取mike:str.section("##",2,2) //初始化 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(false == isOk) { qDebug() << "WriteOnly Error!!!"; tcpSocket->disconnectFromHost(); tcpSocket->close();//关闭套接字 return ;//如果打开文件失败,终端函数 } //弹出对话框,显示接收文件的信息 QString str = QString("接收的文件: [%1: %2kb]").arg(fileName).arg(fileSize/1024); QMessageBox::information(this,"文件信息",str); //设置进度条 ui->progressBar->setMinimum(0); //最小值 ui->progressBar->setMaximum(fileSize/1024);//设置最大值 ui->progressBar->setValue(0);//当前值 } else //文件信息 { qint64 len = file.write(buf); if(len > 0)//接收数据大于0 { recvSize += len;//累计接收大小 QString str = QString::number(recvSize); tcpSocket->write(str.toUtf8().data()); qDebug()<<"str = "< progressBar->setValue(recvSize/1024); if(recvSize == fileSize)//文件接收完毕 { //先给服务器发送(接收文件完成的信息) tcpSocket->write("file done"); file.close(); //关闭文件 QMessageBox::information(this,"完成","文件接收完成!"); //断开连接 tcpSocket->disconnectFromHost(); tcpSocket->close(); } } });}ClientWidget::~ClientWidget(){ delete ui;}void ClientWidget::on_buttonConnect_clicked(){ //获取服务器IP和端口 QString ip = ui->lineEditIP->text(); quint16 port = ui->lineEditPort->text().toInt(); tcpSocket->connectToHost(QHostAddress(ip),port);}
小技巧:
- 有时加载完模块不会立即生效,这时可以点下
锤子
,只编译不运行!或者重新打开项目即可~
转载地址:https://miracle.blog.csdn.net/article/details/105899437 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
做的很好,不错不错
[***.243.131.199]2024年05月01日 14时08分27秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
字符编码,原来是SQL不走索引的元凶之一!
2019-05-02
老板要我开发一个简单的工作流引擎 !
2019-05-02
Spring JPA整合QueryDSL
2019-05-02
Java编程思想笔记——第五章 初始化和清理
2019-05-02
Java编程思想笔记——第十章 内部类
2019-05-02
Java编程思想笔记——第十四章 类型信息
2019-05-02
MySQL学习笔记——慢查询
2019-05-02
Java实现排列组合
2019-05-02
PL/SQL学习笔记之异常
2019-05-02
PL/SQL学习笔记之触发器
2019-05-02
Memcache内存缓存框架
2019-05-02
Python字符编码和转码
2019-05-02
commons-dbutils【不推荐】
2019-05-02
SOCAT端口转发
2019-05-02
docker快速搭建HTTP代理
2019-05-02
前端学习 -- 颜色
2019-05-02
前端学习 -- Css -- 盒子模式
2019-05-02
什么是多线程?看我多线程七十二变,你能记住吗?
2019-05-03
Netty hello world 入门源码分析
2019-05-03
Netty 中的 handler 和 Pipeline
2019-05-03