草庐IT

QT网络编程TCP/UDP开发流程 制作网络调试助手

one goto one 2023-04-18 原文

目录标题

一、开发基础知识

1、QT的网络编程: TCP和UDP

TCP编程需要用到俩个类: QTcpServer 和 QTcpSocket

QTcpSocket类提供了一个TCP套接字

  1. QTcpSocket是QAbstractSocket的一个子类,它允许您建立TCP连接和传输数据流
    注意:TCP套接字不能在QIODevice::Unbuffered模式下打开。

QTcpServer类提供一个基于tcp的服务器
2. 这个类可以接收传入的TCP连接。您可以指定端口或让QTcpServer自动选择一个端口。你可以监听一个特定的地址或所有机器的地址。
3. 调用listen()让服务器侦听传入的连接。然后,每当客户机连接到服务器时,都会发出newConnection()信号。
4. 调用nextPendingConnection()接受挂起的连接作为已连接的QTcpSocket。该函数返回一个指向QAbstractSocket::ConnectedState中的QTcpSocket的指针,您可以使用该指针

2、网络编程接口

1. listen

功能:设置监听数和分配端口,监听客户端连接

bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)

如果address为QHostAddress::Any,服务器将侦听所有网络接口;
为QHostAddress::LocalHost,IPv4本地主机地址,相当于QHostAddress(“127.0.0.1”)
当“port”为“0”时,系统自动分配端口
成功 true;失败 false

2. connect

功能:connect,是QT中的连接函数,将信号发送者sender对象中的信号signal与接受者receiver中的member槽函数联系起来。当指定信号signal时必须使用宏SIGNAL () ,当指定槽函数时必须使用宏SLOT()

connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context,	 
	Func2 slot, Qt::ConnectionType type = Qt::AutoConnection)

二、TCP编程

目标创建一个TCP服务端和一个TCP 客户端简单网络调试助手

1、TCP服务端实现流程

1. 创建项目

创建一个Qt Widgets Application项目项目名称和位置都不要包含中文基类选择QWidget

2. UI设计

接收窗口使用 QPlainTextEdit 并修改为只读
发送窗口使用 Line Edit

3. .h文件中包含头文件

#include <QTcpServer>
#include <QTcpSocket>

4. qmake文件中添加network

QT       += core gui network

5. Widget类构造函数中创建QTcpServer和QTcpSocket的对象用作监听套接字

tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);

tcpServer 和 tcpSocket 对象指针需要在Widget类中声明

QTcpServer *tcpServer;  
QTcpSocket *tcpSocket;

6. 使用listen()方法监听网卡的ip和端口(打开服务器)
侦听IPv4和IPv6接口,端口为portEdit输入的内容

void Widget::on_openBt_clicked()
{
    tcpServer->listen(QHostAddress::Any, ui->portEdit->text().toUInt());
}

7. 如果有新的连接过来,并且连接成功,服务器会触发newConnection()信号,通过槽函数取出连接成功的socket

connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection_Slot())); //连接请求到来,触发newConnection信号,并回调槽函数

void Widget::newConnection_Slot()
{
    while (tcpServer->hasPendingConnections()) //有待连接请求
    {
        tcpSocket = tcpServer->nextPendingConnection();	//连接并返回套接字
        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot())); //套接字有数据到来触发信号readyRead(),回调槽函数
    }
}

8. 如果有数据成功传送过来,对方的通信套接字QTcpSocket会触发readyRead()信号,在槽函数可以进行读取

void Widget::readyRead_Slot()
{
    QString buf = tcpSocket->readAll();	//读数据
    ui->recvEdit->appendPlainText(buf);	//追加显示
}

9. 发送数据

void Widget::on_sendBt_clicked()
{
    tcpSocket->write(ui->sendEdit->text().toLocal8Bit().data());
}

10. 关闭套接字(关闭服务器)

void Widget::on_closeButton_clicked()
{
    tcpSocket->close();
}

2、TCP客户端实现流程

1. 创建项目
基类选择QWidget
2. UI设计

3. .h文件中包含头文件

#include <QTcpServer>
#include <QTcpSocket>

4. qmake文件中添加network

QT       += core gui network

5. Widget类构造函数中创建QTcpSocket的对象用作监听套接字

tcpSocket = new QTcpSocket(this);

tcpSocket 对象指针需要在Widget类中声明

QTcpSocket *tcpSocket;

6. connectTohost()绑定ip和端口号进行连接,连接成功会触发connected()信号,将该信号与槽函数关联

void Widget::on_openBt_clicked()
{
    tcpSocket->connectToHost(ui->ipEdit->text(), ui->portEdit->text().toUInt());
    connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected_Slot()));
}

7. readyRead()信号和槽函数关联

void Widget::connected_Slot()
{
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot()));
}

8. 读数据

void Widget::readyRead_Slot()
{
    QString buf = tcpSocket->readAll();
    ui->recvEdit->appendPlainText(buf);
}

9. 发送数据

void Widget::on_sendBt_clicked()
{
    tcpSocket->write(ui->sendEdit->text().toLocal8Bit());
}

10. 关闭套接字

void Widget::on_sendBt_clicked()
{
    tcpSocket->write(ui->sendEdit->text().toLocal8Bit());
}

三、UDP编程

udp通信这里不分服务端和客户端

1. 创建项目
基类选择QWidget
2. UI设计

3. .h文件中包含头文件

#include <QUdpSocket>

4. qmake文件中添加network

QT       += core gui network

5. Widget类构造函数中创建QTcpSocket的对象用作监听套接字

udpSocket = new QUdpSocket(this); //获取套接字

udpsocket对象指针需要在Widget类中声明

QUdpSocket *udpSocket;

6. bind()设置本地端口号,准备读信号与槽函数关联(打开udp连接)

void Widget::on_openBt_clicked()
{
    udpSocket->bind(ui->localPort->text().toUInt());  //绑定本地端口

    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_slot()));  //准备读信号与槽函数关联
}

7. 读数据

void Widget::readyRead_slot()
{
    while(udpSocket->hasPendingDatagrams()) {   //udp与tcp不同,udp需要判断数据是否读完
        QByteArray array;
        array.resize(udpSocket->pendingDatagramSize()); //调整array数组大小与数据报大小一致
        udpSocket->readDatagram(array.data(), array.size()); //读取数据报到array.data中、

        QString buf = array.data(); //将数据类型转换成string类型
        ui->recvEdit->appendPlainText(buf); //将数据显示到接收窗口
    }
}

8. 发送数据

void Widget::on_sendBt_clicked()
{
    QHostAddress address;

    address.setAddress(ui->aimIp->text());
    udpSocket->writeDatagram(ui->sendEdit->text().toLocal8Bit().data(), //发送内容 const char *
                             ui->sendEdit->text().length(), //内容长度 qint64
                             address, //目标IP const QHostAddress &
                             ui->aimPort->text().toUInt()); //目标端口 quint16
}
  1. 关闭套接字
udpSocket->close();

四、源码

1. TCP服务端

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QString>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    QTcpServer *tcpServer;  //声明套接字
    QTcpSocket *tcpSocket;

private slots:  //槽函数声明
    void newConnection_Slot();
    void readyRead_Slot();
    void on_openBt_clicked();
    void on_closeButton_clicked();
    void on_sendBt_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    tcpServer = new QTcpServer(this);   //获取套接字
    tcpSocket = new QTcpSocket(this);

    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection_Slot())); //新连接请求信号与连接槽函数关联
}

void Widget::newConnection_Slot()
{
    while (tcpServer->hasPendingConnections())  //有待连接请求
    {
        tcpSocket = tcpServer->nextPendingConnection(); //连接并返回套接字
        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot())); //读准备信号与读槽函数关联
    }
}

void Widget::readyRead_Slot()   //读取数据并显示到接收窗口
{
    QString buf = tcpSocket->readAll();
    ui->recvEdit->appendPlainText(buf);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_openBt_clicked()   //打开服务器
{
    tcpServer->listen(QHostAddress::Any, ui->portEdit->text().toUInt()); //监听接口,设计端口号
}

void Widget::on_closeButton_clicked()   //关闭套接字
{
    tcpSocket->close();
}

void Widget::on_sendBt_clicked()   //发送数据
{
    tcpSocket->write(ui->sendEdit->text().toLocal8Bit().data());
}

2. TCP客户端

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>
#include <QString>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);

    QTcpSocket *tcpSocket;

    ~Widget();

private slots:
    void connected_Slot();
    void readyRead_Slot();
    void on_closeBt_clicked();
    void on_sendBt_clicked();
    void on_openBt_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    tcpSocket = new QTcpSocket(this);	//获取套接字
}

Widget::~Widget()
{
    delete ui;
}

void Widget::connected_Slot()
{
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot()));	//读准备信号关联槽函数
}

void Widget::readyRead_Slot()	//读数据
{
    QString buf = tcpSocket->readAll();
    ui->recvEdit->appendPlainText(buf);
}

void Widget::on_sendBt_clicked()	//发送数据
{
    tcpSocket->write(ui->sendEdit->text().toLocal8Bit());
}

void Widget::on_closeBt_clicked()	//关闭套接字
{
    tcpSocket->close();
}

void Widget::on_openBt_clicked()	//连接服务器,连接成功信号和槽函数关联
{
    tcpSocket->connectToHost(ui->ipEdit->text(), ui->portEdit->text().toUInt());
    connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected_Slot()));  //可替换如下
    //connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_Slot()));
}

3. UDP

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QString>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    QUdpSocket *udpSocket;

private slots:
    void on_openBt_clicked();
    void readyRead_slot();
    void on_sendBt_clicked();
    void on_closeBt_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    udpSocket = new QUdpSocket(this); //获取套接字
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_openBt_clicked()
{
    udpSocket->bind(ui->localPort->text().toUInt());  //绑定本地端口

    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead_slot()));  //准备读信号与槽函数关联
}

void Widget::readyRead_slot()
{
    while(udpSocket->hasPendingDatagrams()) {   //udp与tcp不同,udp需要判断数据是否读完
        QByteArray array;
        array.resize(udpSocket->pendingDatagramSize()); //调整array数组大小与数据报大小一致
        udpSocket->readDatagram(array.data(), array.size()); //读取数据报到array.data中、

        QString buf = array.data(); //将数据类型转换成string类型
        ui->recvEdit->appendPlainText(buf); //将数据显示到接收窗口
    }
}

void Widget::on_sendBt_clicked()
{
    QHostAddress address;

    address.setAddress(ui->aimIp->text());
    udpSocket->writeDatagram(ui->sendEdit->text().toLocal8Bit().data(), //发送内容 const char *
                             ui->sendEdit->text().length(), //内容长度 qint64
                             address, //目标IP const QHostAddress &
                             ui->aimPort->text().toUInt()); //目标端口 quint16
}

void Widget::on_closeBt_clicked()
{
    udpSocket->close(); //关闭套接字
}

有关QT网络编程TCP/UDP开发流程 制作网络调试助手的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  5. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  6. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  7. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  8. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

    Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

随机推荐