Qt网络编程-TCP与UDP

网络基础

TCP与UDP基础

关于TCP与UDP的基础这里就不过多介绍了,具体可以查看对应百度百科介绍:

TCP(传输控制协议)_百度百科 (baidu.com)

UDP_百度百科 (baidu.com)

需要知道这两者的区别:

  1. 可靠性:

    • TCP:TCP 是一种面向连接的协议,它提供可靠的数据传输。它使用序号、确认和重传等机制来确保数据的可靠性,以及按序传递数据包。如果数据包丢失或损坏,TCP 会自动进行重传,直到数据被正确接收。
    • UDP:UDP 是一种无连接的协议,它不提供数据包的可靠性保证。UDP 发送的数据包可能丢失、重复或无序,因此它不适合对数据可靠性要求很高的应用。
  2. 连接性:

    • TCP:TCP 是面向连接的,它在通信双方建立连接后(有客户端与服务器之分)才能进行数据传输。TCP 连接是可靠的、有序的、全双工的,通信双方可以进行双向通信。
    • UDP:UDP 是无连接的,它不需要在通信双方之间建立连接(没有客户端与服务器之分)。每个 UDP 数据包都是独立的,发送者和接收者之间没有持久的连接。
  3. 效率:

    • TCP:TCP 通过使用流量控制和拥塞控制等机制,以及连接的建立和维护,会产生一定的开销。因此,TCP 在可靠性和有序性方面提供了较高的保证,但可能会牺牲一些效率。
    • UDP:UDP 不需要进行连接的建立和维护,也不需要进行重传或流量控制等操作,因此它通常比 TCP 具有更低的开销和更高的效率。

网络通信以上两者都绕不开IP地址与端口这两个。

开发调试所需工具

一般情况需要网络调试助手或者wireshark抓包工具,网络调试助手我用的是NetAssist。关于NetAssist和WireShark怎么使用,后面会介绍。

windows与linux如何查看和修改本地的IP端口

Window

cmd命令行:ipconfig/all

修改:

设置选中“网络和Internet”

Linux

命令行:ifconfig -a:

static QStringList getIPAddresses() {
    QStringList addresses;
    for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol)
            addresses.append(address.toString());
    }
    return addresses;
}

或者直接查看网络设置。

使用Qt函数获取

Qt要使用网络模块记得工程文件添加:QT += network

static QStringList getIPAddresses() {
    QStringList addresses;
    for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
        if (address.protocol() == QAbstractSocket::IPv4Protocol)
            addresses.append(address.toString());
    }
    return addresses;
}

编译运行查看打印:

Windows与Linux查看本地连接情况

Windows和Linux都需要借助netstat命令,但是两者稍微有一些不一样。

Windows

比如我使用刚刚的调试助手NetAssist,建立一个tcp服务器,然后监听IP:192.168.5.1,端口:8080。

查看一下这个服务器是否监听成功,命令行输入:netstat -antp TCP(‘p’指定对应协议,后面需要接协议类型TCP或UDP)

 或者直接输入命令 netstat -antp TCP|findstr 8080:

再起一个调试助手以客户端的形式连接这个服务器:

 再次输入: netstat -antp TCP|findstr 8080:

能够查看到刚刚建立的连接。

查看对应链接是哪个应用建立的,先输入: netstat -antpo TCP|findstr 8080:

然后使用tasklist查看对应进程: 

如果是UDP改为 netstat -antp UDP|findstr 8080 即可

Linux 

Linux 的netstat的命令指定对应协议不需要 -p TCP或者-p UDP,而是-t就是TCP,-u就是UDP,如下图所示:

 Qt实现TCP

因为TCP是需要建立链接,分客户端和服务器端的,所以需要分别编写。

服务器端

服务器由QTcpServer来实现,QTcpServer的信号:

需要注意newConnection这个信号,当有客户端连接这个服务器时,会触发这个信号。

所有的方法:


需要注意的几个方法:


bool  listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
 
监听对应IP和端口,IP为空则默认监听any

close()

停止监听

bool isListening() const
 
是否正在监听

QHostAddress serverAddress() const
 
监听的IP地址

quint16 serverPort() const
 
监听的端口

void setMaxPendingConnections(int numConnections)
 
设置允许建立的最大连接数

比如监听IP 127.0.0.1 端口 8080:

QTcpServer server;

server.listen(QHostAddress("127.0.0.1"),8080); 

写一个简单的例子。

ui:

头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private slots:
    void on_listen_clicked();
    void on_disconnect_clicked();
    void on_send_clicked();
    void newConnection();
private:
    Ui::MainWindow *ui;
    QTcpServer *m_Server;
    QTcpServer *m_Server1;
    QList<QTcpSocket *> m_Sockets;
    void showLog(const QString &log);
};
#endif // MAINWINDOW_H

 源文件:

#include "mainwindow.h"
#include <QDateTime>
#include <QHostAddress>
#include <QNetworkInterface>
#include "ui_mainwindow.h"
static QStringList getIPAddresses() {
  QStringList addresses;
  for (const QHostAddress &address : QNetworkInterface::allAddresses()) {
    if (address.protocol() == QAbstractSocket::IPv4Protocol)
      addresses.append(address.toString());
  }
  return addresses;
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);
  setWindowTitle("TcpServer");
  ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
  ui->localIp->addItems(getIPAddresses());
  ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
  m_Server = new QTcpServer;
  connect(m_Server, &QTcpServer::newConnection, this,
          &MainWindow::newConnection);
  for (QTcpSocket *socket : m_Sockets) {
    connect(socket, &QTcpSocket::readyRead, [=]() {
      showLog(QString("%1:%2:%3")
                  .arg(socket->peerAddress().toString())
                  .arg(socket->peerPort())
                  .arg(QString(socket->readAll().toHex())));
    });
    connect(socket, &QTcpSocket::disconnected, [=]() {
      showLog(QString("disconnect:%1:%2:%3")
                  .arg(socket->peerAddress().toString())
                  .arg(socket->peerPort())
                  .arg(QString(socket->readAll().toHex())));
      for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
        QTableWidgetItem *ipItem = ui->tableWidget->item(i, 0);
        QTableWidgetItem *portItem = ui->tableWidget->item(i, 1);
        if (nullptr != ipItem && nullptr != portItem) {
          if (ipItem->text() == socket->peerAddress().toString() &&
              portItem->text() == socket->peerPort()) {
            ui->tableWidget->removeRow(i);
            break;
          }
        }
      }
    });
    connect(
        socket,
        static_cast<void (QTcpSocket::*)(const QAbstractSocket::SocketError)>(
            &QTcpSocket::error),
        [=](QAbstractSocket::SocketError error) {
          qDebug() << "error:" << error;
          showLog("error:" + QString::number(int(error)));
        });
    connect(socket, &QTcpSocket::stateChanged,
            [=](QAbstractSocket::SocketState state) {
              qDebug() << "stateChanged:" << state;
              showLog("stateChanged:" + QString::number(int(state)));
            });
  }
}

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

void MainWindow::on_listen_clicked() {
  if (ui->listen->text() == "listen") {
    if (m_Server->listen(QHostAddress(ui->localIp->currentText()),
                         ui->localPort->value()))
      ui->listen->setText("listening");
    else
      ui->textEdit->append("listen fail");
  } else {
    for (QTcpSocket *socket : m_Sockets) {
      socket->close();
      socket->disconnectFromHost();
    }
    m_Server->close();
    ui->listen->setText("listen");
  }
}

void MainWindow::on_disconnect_clicked() {
  int row = ui->tableWidget->currentRow();
  if (-1 != row) {
    QTableWidgetItem *ipItem = ui->tableWidget->item(row, 0);
    QTableWidgetItem *portItem = ui->tableWidget->item(row, 1);
    if (nullptr != ipItem && nullptr != portItem) {
      QString ip = ipItem->text();
      quint16 port = portItem->text().toUShort();
      for (QTcpSocket *socket : m_Sockets) {
        if (ip == socket->peerAddress().toString() &&
            port == socket->peerPort()) {
          socket->close();
          socket->disconnectFromHost();
          break;
        }
      }
    }
  }
}

void MainWindow::on_send_clicked() {
  QByteArray ba = ui->send->text().toUtf8();
  int row = ui->tableWidget->currentRow();
  if (-1 != row) {
    QTableWidgetItem *ipItem = ui->tableWidget->item(row, 0);
    QTableWidgetItem *portItem = ui->tableWidget->item(row, 1);
    if (nullptr != ipItem && nullptr != portItem) {
      QString ip = ipItem->text();
      quint16 port = portItem->text().toUShort();
      for (QTcpSocket *socket : m_Sockets) {
        if (ip == socket->peerAddress().toString() &&
            port == socket->peerPort()) {
          socket->write(ba);
          break;
        }
      }
    }
  }
}

void MainWindow::newConnection() {
  QTcpSocket *socket = m_Server->nextPendingConnection();
  m_Sockets.append(socket);
  // m_TcpSocket = socket;
  int row = ui->tableWidget->rowCount();
  ui->tableWidget->insertRow(row);
  QTableWidgetItem *ipItem =
      new QTableWidgetItem(socket->peerAddress().toString());
  QTableWidgetItem *portItem =
      new QTableWidgetItem(QString::number(socket->peerPort()));
  qDebug() << socket->peerAddress().toString() << "," << socket->peerPort()
           << "," << socket->peerName();
  ui->tableWidget->setItem(row, 0, ipItem);
  ui->tableWidget->setItem(row, 1, portItem);
  connect(socket, &QTcpSocket::disconnected, [=]() {
    showLog(QString("disconnected:%1:%2")
                .arg(socket->peerAddress().toString())
                .arg(socket->peerPort()));
    // m_TcpSocket = nullptr;
    for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
      QTableWidgetItem *ipItem = ui->tableWidget->item(i, 0);
      QTableWidgetItem *portItem = ui->tableWidget->item(i, 1);
      if (nullptr != ipItem && nullptr != portItem) {
        if (ipItem->text() == socket->peerAddress().toString() &&
            portItem->text() == QString::number(socket->peerPort())) {
          ui->tableWidget->removeRow(i);
          break;
        }
      }
    }
  });
}

void MainWindow::showLog(const QString &log) {
  ui->textEdit->append(
      QString("%1:%2")
          .arg(QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz"))
          .arg(log));
}

有客户端连接后,会触发newConnection这个信号,然后在槽函数中使用nextPendingConnection()这个方法获取对应的客户端QTcpSocket 指针对象。客户端发送消息后会触发readyRead这个信号,使用QTcpSocket的readAll获取发送的信息。编译运行,输入监听的ip和端口然后点击listen按钮然后使用命令查看是否开始监听对应ip和端口:

然后使用调试助手作为客户端连接这个服务器:

 

可以看到触发了newConnection信号,然后获取对应客户端对象将其信息显示到了的表格上面。

实验链接的建立与取消以及消息的互相发送:

可以实现对应的通讯。 

以上是使用调试助手,也可以使用WireShark抓包查看发送和接受的数据。需要注意的是如果客户端和服务器都在本地自己发自己收是用WireShark抓不到的。

这里简单说一下wireshark的用法,首先选择需要抓取的网卡:

比如ping就是走的tcp,测试时我是用 虚拟机ping我的主机,如何使vmware虚拟机和主机ping通可以参考这位博主的博客:实现虚拟机(VM15.5.0)与本机相互通信_vmware和主机怎样才能ping通-CSDN博客

然后查看wireshark可以看到ping的报文:

因为我的主机ip192.168.1.3,虚拟机ip是192.168.1.4,可以输入“ip.src==192.168.1.4 && ip.dst==192.168.1.3” 来过滤:

 同样,我使用虚拟机和主机建立tcp连接然后发送消息也是可以抓到:

 

 

X

客户端

客户端由QTcpSocket实现,QTcpSocket继承自QAbstractSocket,比如上文中在虚拟机中建立一个tcp服务器,监听ip192.168.1.3,端口12345:

QTcpSocket *socket =new QTcpSocket;
socket->connectToHost(QHostAddress(),12345);
if(socket->waitForConnected())
{
  //TODO 连接成功
}
else
{
  //TODO 连接失败
}

connect(socket,&QTcpSocket::disconnected,[=](){
//TODO 处理连接断开
});
connect(socket, &QTcpSocket::readyRead, [=]() {
        QByteArray receiveData=socket->readAll();
        //TODO 处理接收的数据
    });

QByteArray sendData;
//TODO 处理发送数据
//发送数据
socket->write(sendData);
//断开连接,两种方式
socket->abort;//强制中断连接
socket->disconnectFromHost();//不会马上关闭连接,等待资源释放后才会中断连接

另外客户端套接字可以绑定bind对应ip和端口,如果没有绑定,则系统会使用之绑定一个随即的可用的ip和端口 :

Qt实现UDP

因为UDP不用建立连接,不用分服务器和客户端,所以对应Qt的UDP部分,只需要使用QUdpSocket一个即可:

QUdpSocket同QTcpSocket一样都继承自QAbstractSocket,使用UDP通信前,对应udp套接字需要绑定对应ip与端口,然后发送数据时需要知道对方的ip与端口(UDP分单播、组播与广播,这里只说单播,组播与广播后面博客再写):

QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress("192.168.1.3"), 12345);
connect(udpSocket, &QUdpSocket::readyRead,[=](){

  while (udpSocket->hasPendingDatagrams()) {
      QByteArray data;
      QHostAddress host;
      quint16 port;
      data.resize(udpSocket->pendingDatagramSize());
      udpSocket->readDatagram(data.data(), data.size(), &host, &port);
      //TODO 处理接受数据
     }
});
QByteArray sendData;
//TODO 处理发送数据
udpSocket->writeDatagram(sendData, QHostAddress("192.168.1.4"),
                          12345);
//取消绑定
udpSocket->unbind();

使用网络助手模拟udp通信:

使用自己写的udp程序在虚拟机中与之通信:

 使用wireshark抓包:

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

报道|2024 INFORMS Franz Edelman奖决赛名单

编者按 近日&#xff0c;2024年INFORMS Franz Edelman奖公布了决赛名单。Franz Edelman奖旨在促进能拯救生命、节省金钱以及解决问题的研究。决赛名单包含了零售、航空、食物供应链以及可持续发展等领域的优秀研究。 NFORMS近日发文&#xff0c;公布了进入2024 INFORMS Franz E…

如何在 emacs 上开始使用 Tree-Sitter (archlinux)

文章目录 如何在emacs上开始使用Tree-Sitter&#xff08;archlinux&#xff09; 如何在emacs上开始使用Tree-Sitter&#xff08;archlinux&#xff09; 在archlinux上使用比windows上不知道要方便多少倍&#xff01; $ sudo pacman -S emacs $ sudo pacman -S tree-sitter这里…

了解物联网漏洞与家庭网络入侵之间的联系

我们的家庭正日益成为一个由互连设备组成的网络。从智能恒温器到联网冰箱&#xff0c;物联网 (IoT) 彻底改变了我们与家庭环境互动的方式。 随着技术进步带来了新的挑战&#xff1a;这些设备容易受到网络威胁以及随之而来的家庭网络入侵风险。 在这篇博文中&#xff0c;我们将…

Quartus工程的qsf配置约束文件介绍

一、qsf文件概述 qsf&#xff1a;Quartus Setting File&#xff0c;是Quartus工程的配置文件&#xff1b; 包含一个Quartus工程的所有约束&#xff0c;包括工程的软件版本信息、FPGA器件信息、引脚约分配、引脚电平分配&#xff0c;编译约束和用于Classic TimingAnalyzer的时…

Mac电脑如何通过终端隐藏应用程序?

在我们使用Mac电脑的时候难免会遇到想要不想看到某个应用程序又不想卸载它们。值得庆幸的是&#xff0c;macOS具有一些强大的文件管理功能&#xff0c;允许用户轻松隐藏&#xff08;以及稍后显示&#xff09;文件甚至应用程序。 那么&#xff0c;Mac电脑如何通过终端隐藏应用程…

《动手学深度学习(PyTorch版)》笔记7.6

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

XGB-6: 单调性约束Monotonic Constraints

在建模问题或项目中&#xff0c;通常情况下&#xff0c;可接受模型的函数形式会以某种方式受到约束。这可能是由于业务考虑&#xff0c;或者由于正在研究的科学问题的类型。在某些情况下&#xff0c;如果对真实关系有非常强烈的先验信念&#xff0c;可以使用约束来提高模型的预…

Redis -- 安装客户端redis-plus-plus

目录 访问reids客户端github链接 安装git 如何安装&#xff1f; 下载/编译、安装客户端 安装过程中可能遇到的问题 访问reids客户端github链接 GitHub - sewenew/redis-plus-plus: Redis client written in CRedis client written in C. Contribute to sewenew/redis-p…

Javaweb之SpringBootWeb案例之异常处理功能的详细解析

3. 异常处理 3.1 当前问题 登录功能和登录校验功能我们都实现了&#xff0c;下面我们学习下今天最后一块技术点&#xff1a;异常处理。首先我们先来看一下系统出现异常之后会发生什么现象&#xff0c;再来介绍异常处理的方案。 我们打开浏览器&#xff0c;访问系统中的新增部…

运维的利器--监控--zabbix--第一步:建设zabbix

文章目录 准备工作安装要求安装包获取安装环境 安装工作一、zabbix server服务端安装1.安装mysql2.安装zabbix server及配置环境3.设置并访问zabbix页面5.配置自我监控二、被监控端zabbix agent安装三、在服务端中添加被监控端 思维导图 准备工作 安装要求 为啥要确保正常上网…

自学Python第二十二天- Django框架(六) django的实用插件:cron、APScheduler

django-crontab 和 django-cron 有时候需要django在后台不断的执行一个任务&#xff0c;简单的可以通过中间件来实现&#xff0c;但是中间件是根据请求触发的。如果需要定时执行任务&#xff0c;则需要使用到一些插件。 django-crontab 和 django-cron 是常用的用于处理定时任…

three.js 匀速动画(向量表示速度)

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div>1. 匀速动画(向量表示速度)</div…

【转载】原生社区交友婚恋视频即时通讯双端APP源码 ONE兔2.0版

原生社区交友婚恋视频即时通讯双端APP源码下载ONE兔2.0版 包含后端、H5源码源码&#xff0c;Android源码&#xff0c;IOS源码

LabVIEW热电偶自动校准系统

设计并实现一套基于LabVIEW平台的工业热电偶自动校准系统&#xff0c;通过自动化技术提高校准效率和精度&#xff0c;降低人力成本&#xff0c;确保温度测量的准确性和可靠性。 工业生产过程中&#xff0c;温度的准确测量对产品质量控制至关重要。传统的热电偶校准方式依赖人工…

VS Code中主程序C文件引用了另一个.h头文件,编译时报错找不到函数

目录 一、问题描述二、问题原因三、解决方法四、扩展五、通过CMake进行配置 一、问题描述 VS Code中主程序C文件引用了另一个.h头文件&#xff0c;编译时报错找不到函数 主程序 main.c #include <stdio.h> #include "sumaa.h"int main(int, char**){printf(&q…

阿里云服务器价格表2024最新版CPU内存带宽报价

2024年2月阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核…

2024-02-08 Unity 编辑器开发之编辑器拓展1 —— 自定义菜单栏与窗口

文章目录 1 特殊文件夹 Editor2 在 Unity 菜单栏中添加自定义页签3 在 Hierarchy 窗口中添加自定义页签4 在 Project 窗口中添加自定义页签5 在菜单栏的 Component 菜单添加脚本6 在 Inspector 为脚本右键添加菜单7 加入快捷键8 小结 1 特殊文件夹 Editor ​ Editor 文件夹是 …

netty-websocket扩展协议及token鉴权补充

文章源码&#xff1a;gitee 源码部分可以看上一篇文章中的源码分析netty-websocket 鉴权token及统一请求和响应头&#xff08;鉴权控制器&#xff09; 最近刚好没事&#xff0c;看到有朋友说自定义协议好搞&#xff0c;我就想了想&#xff0c;发现上面那种方式实现确实麻烦&…

Spring Boot 笔记 003 Bean注册

使用Idea导入第三方jar包 在porn.xml种添加的第三方jar包依赖&#xff0c;并刷新 可以在启动类中尝试调用 以上放到启动类中&#xff0c;不推荐&#xff0c;建议创建一个专门定义的类 package com.geji.config;import cn.itcast.pojo.Country; import cn.itcast.pojo.Province;…

python烟花绘制,春节祝福

春节将至&#xff0c;写一个烟花程序给亲近的人 核心逻辑 烟花类&#xff1a; 定义烟花的颜色&#xff0c;更新烟花的轨迹&#xff0c;爆炸&#xff0c;消失等功能&#xff0c;在烟花爆炸的同时也涉及到粒子的创建 class Firework:def __init__(self):# 随机颜色self.colou…
最新文章