Qt---Socket通信

一、TCP/IP通信     

  • 在Qt中实现TCP/IP服务器端通信的流程:

1. 创建套接字

2. 将套接字设置为监听模式

3. 等待并接受客户端请求

        可以通过QTcpServer提供的void  newConnection()信号来检测是否有连接请求,如果有可以在对应的槽函数中调用nextPendingConnection函数获取到客户端的Socket信息(返回值为QTcpSocket*类型指针),通过此套接字与客户端之间进行通信。

4. 接收或者向客户端发送数据

        接收数据:使用read()或者readAll()函数

        发送数据:使用write()函数

  • 客户端通信流程:

1. 创建套接字

2. 连接服务器

        可以使用QTcpSocket类的connectToHost()函数来连接服务器。

3. 向服务器发送或者接受数据

linuxTCP通信流程和QtTCP通信流程对比: 

Qt TCP通信流程
Linux TCP通信流程

TCP/IP通信的实现

服务器端:

首先.pro文件中加上netxwok:

QT       += core gui network

serverwidget.ui:

选中按钮,右击转到槽可直接对该按钮进行程序设置

serverwidget.h:

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H#include <QWidget>
#include<QTcpServer>//监听套接字
#include<QTcpSocket>//通信套接字QT_BEGIN_NAMESPACE
namespace Ui { class ServerWidget; }
QT_END_NAMESPACEclass ServerWidget : public QWidget
{Q_OBJECTpublic:ServerWidget(QWidget *parent = nullptr);~ServerWidget();private slots:void on_buttonSend_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;//监听套接字,指定父对象,让其自动回收tcpServer = new QTcpServer(this);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();//给对方发送数据,使用套接字是tcpSockettcpSocket->write(str.toUtf8().data());
}void ServerWidget::on_buttonClose_clicked()
{if (NULL == tcpSocket){return;}//主动和客户端断开连接tcpSocket->disconnectFromHost();tcpSocket->close();tcpSocket = NULL;
}

运行服务器之后直接点击send,会出现错误

解决:

对套接字在连接之前和断开连接之后置为空,如上代码所示

客户端:

首先修改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.ui:

clientwidget.h:

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H#include <QWidget>
#include<QTcpSocket>//通信套接字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"
#include<QHostAddress>ClientWidget::ClientWidget(QWidget *parent) :QWidget(parent),ui(new Ui::ClientWidget)
{ui->setupUi(this);tcpSocket = NULL;//分配空间,指定父对象tcpSocket = new QTcpSocket(this);setWindowTitle("客户端");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();
}

输出如下所示:

二、UDP通信

使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。类似地,服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达

在UDP通信中,服务器端和客户端的概念已经显得有些淡化,两部分做的工作都大致相同:

1. 创建套接字

2. 绑定套接字

        在UDP中如果需要接收数据需要对套接字进行绑定,只发送数据则不需要对套接字进行绑定。通过调用bind()函数将套接字绑定到指定端口上。

3. 接收或者发送数据

接收数据:使用readDatagram()接收数据

函数声明如下:

qint64 readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0)        

参数:

        data: 接收数据的缓存地址

        maxSize: 缓存接收的最大字节数

        address: 数据发送方的地址(一般使用提供的默认值)

        port: 数据发送方的端口号(一般使用提供的默认值)

发送数据:使用writeDatagram()函数发送数据
函数声明如下:

qint64 writeDatagram(const QByteArray & datagram, const QHostAddress & host, quint16 port)

参数:

        datagram:要发送的字符串

        host:数据接收方的地址

        port:数据接收方的端口号

linuxUDP通信流程和QtUDP通信流程对比: 

Qt UDP通信流程

Linux UDP通信流

UDP通信的实现

服务端和客户端代码类似 

widget.ui 

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QUdpSocket>//UDP套接字QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void dealMsg();//槽函数处理对方发送过来的数据private slots:void on_bottonSend_clicked();private:Ui::Widget *ui;QUdpSocket *udpSocket;//UDP套接字
};
#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);//分配空间,指定父对象udpSocket = new QUdpSocket(this);//绑定udpSocket->bind(9999);//    udpSocket->bind(QHostAddress::AnyIPv4,9999);
//    //加入某个组播
//    //组播地址是D类地址
//    udpSocket->joinMulticastGroup(QHostAddress("224.0.0.2"));
//    //udpSocket->leaveMulticastGroup()//退出组播setWindowTitle("服务器端口为:9999");//当对方成功发送数据过来//自动触发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:8888]aaaaQString str = QString("[%1:%2] %3").arg(cliAddr.toString()).arg(port).arg(buf);//给编辑区设置内容ui->textEdit->setText(str);}
}Widget::~Widget()
{delete ui;
}//发送按钮
void Widget::on_bottonSend_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);
}

同理,再写一个客户端进行通信,输出如下所示:

三、TCP/IP 和 UDP的区别

TCP/IP

UDP

是否连接

面向连接

无连接

传输方式

基于流

基于数据报

传输可靠性

可靠

不可靠

传输效率

效率低

效率高

能否广播

不能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/702040.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AXI GPIO IP核配置详解

AXI GPIO&#xff08;AXI General-Purpose Input/Output&#xff09;设计提供了一个通用的输入/输出接口&#xff0c;该接口连接到一个AXI4-Lite接口。AXI GPIO可以被配置为单通道或双通道设备&#xff0c;每个通道的位宽可以独立配置。 端口&#xff08;即GPIO引脚&#xff0…

Vue实战技巧 —— 企业开发实战中的常见疑难问题

Vue企业开发实战中的常见疑难问题 1. 解决Vue动态路由参数变化&#xff0c;页面数据不更新2. vue组件里定时器销毁问题3. vue实现按需加载组件的两种方式4. 组件之间&#xff0c;父子组件之间的通信方案5. Vue中获取当前父元素&#xff0c;子元素&#xff0c;兄弟元素6. 开发环…

Docker 使用 CentOS 镜像

使用 docker run 直接运行 CentOS 7 镜像&#xff0c;并登录 bash。 C:\Users\yhu>docker run -it centos:centos7 bash Unable to find image centos:centos7 locally centos7: Pulling from library/centos 2d473b07cdd5: Pull complete Digest: sha256:be65f488b7764ad36…

风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码)完整代码风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码) 完整代码 clc; clear close allX = xlsread(风电场预测.xlsx)

web学习记录--(5.14)

1.Sublime安装与汉化 直接点击windows即可下载&#xff0c;安装即可 Thank You - Sublime Text 汉化 Install Package ChineseLocalzation 2.PHPstorm下载以及激活,汉化 直接下载&#xff0c;然后找激活码激活即可 汉化 plugins&#xff08;插件&#xff09;/chinese&…

Kotlin协程实战指南:解锁Android开发高效能新时代

前言 在移动互联网的狂飙突进之中&#xff0c;Android开发领域如同站在风口的勇士&#xff0c;不断接受技术迭代与创新的双重洗礼。在这个快速变化的市场里&#xff0c;用户对应用性能和体验的期待水涨船高&#xff0c;开发者们面临的挑战也越来越大&#xff1a;如何在功能的丰…

【Redis7】10大数据类型之Bitfield类型

文章目录 1. Bitfield简介2. BITFIELD key [GET type offset]3. BITFIELD key [SET type offset value]4. BITFIELD key [INCRBY type offset increment] 1. Bitfield简介 Bitfield(位域)命令可以将一个 Redis 字符串看作是一个由二进制位组成的数组&#xff0c; 并对这个数组…

React 状态管理库深度对比:在做技术选型的时候如何选择合适的状态库,nolan出品

掘金链接&#xff1a;https://juejin.cn/post/7368288987642232872 1,简介 在状态共享这方面&#xff0c;不像 Vuex&#xff0c;React 的官方并没有强力推荐某种封装方案&#xff0c;所以 React 的状态管理工具五花八门&#xff0c;百花齐放&#xff0c; react-redux、dva、C…

全像宇宙投影第三部时间与空间(全文)下载

当这个人向空中凝视时&#xff0c;他所在的房间渐渐变得透明而朦胧&#xff0c;空中渐渐浮现一个久远前景像。突然他觉得自己在皇宫中庭内&#xff0c;他面前站著一位年轻女士非常美丽&#xff0c;有著橄榄色的皮肤。他可以见到她的颈项、手腕、脚踝上都挂著金饰&#xff0c;还…

基于HTTP GET方式获取网络时间的实现

上一节&#xff0c;我们介绍了基于NTP服务器获取网络时间的例子&#xff0c;但在有些情况下&#xff0c;比如我最近在使用RNDIS协议通过4G模块上网&#xff0c;这个协议不支持UDP协议&#xff0c;所以就用不了NTP服务器。或者有时候我们需要有更多的网络时间获取方式&#xff0…

Dart 3.4 发布:Wasm Native Macros(宏)

Google I/O 的结束&#xff0c;除了 Flutter 3.22 的发布 &#xff0c;Dart 3.4 也迎来了它是「史诗级」的更新&#xff0c;之所以这么说&#xff0c;就是因为 Wasm Native 的落地和 Macros 的实验性展示。 在此之前&#xff0c;其实我也提前整理过一些对应的内容&#xff0c;…

平安养老险深圳分公司携手福海街道开展5.12防灾减灾活动

在构建和谐社会、倡导人文关怀的当下&#xff0c;平安养老险深圳分公司以高度的社会责任感和深厚的人文情怀&#xff0c;持续关注老年人的健康与安全。在今年“5.12防灾减灾日”来临之际&#xff0c;公司积极响应倡议&#xff0c;于5月10日携手福海街道举办了一场别开生面的消防…