14-3_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP 单播和广播

news2024/11/18 13:56:10

文章目录

  • 1.UDP通信概述
  • 2. UDP 单播和广播
    • 2.1 UDP 通信实例程序功能
    • 2.2 主窗口类定义和构造函数
    • 2.3 UDP通信的实现
    • 2.4 源码
      • 2.4.1 可视化UI设计
      • 2.4.2 mainwindow.h
      • 2.4.3 mainwindow.cpp

1.UDP通信概述

UDP(User Datagram Protocol,用户数据报协议)是轻量的、不可靠的、面向数据报 (datagram)、无连接的协议,它可以用于对可靠性要求不高的场合。与 TCP 通信不同,两个程序之间进行 UDP 通信无需预先建立持久的 socket 连接,UDP 每次发送数据报都需要指定目标地址和端口(如图14-6 所示)。

在这里插入图片描述

QUdpSocket 类用于实现 UDP 通信,它从 QAbstractSocket 类继承,因而与 QTcpSocket 共享大部分的接口函数。主要区别是 QUdpSocket 以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般少于512 字节,每个数据报包含发送者和接收者的 IP 地址和端口等信息。
要进行 UDP 数据接收,要用 QUdpSocket::bind()函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射readyRead()信号,使用readDatagram()函数来读取接收到的数据报。

UDP 消息传送有单播、广播、组播三种模式,其示意图如图 14-7 所示

在这里插入图片描述

  • 单播 (unicast)模式:一个 UDP 客户端发出的数据报只发送到另一个指定地址和端口的UDP 客户端,是一对一的数据传输。

  • 广播(broadcast)模式:一个 UDP 客户端发出的数据报,在同一网络范围内其他所有的UDP 客户端都可以收到。QUdpSocket 支持IPv4 广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为 QHostAddress::Broadcast,一般的广播地址是 255.255.255.255

  • 组播 (multicast)模式:也称为多播。UDP 客户端加入到另一个组播IP 地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于 QQ 群的功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能,加入多播组后,UDP 数据的收发和UDP数据收发方法一样。

使用广播和多播模式,UDP 可以实现一些比较灵活的通信功能,而 TCP 通信只有单播模式没有广播和多播模式。所以,UDP 通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP 通信的。

QUdpSocket 类从QAbstractSocket 继承而来,但是又定义了较多新的功能函数用于实现 UDP特有的一些功能,如数据报读写和多播通信功能。QUdpSocket 没有定义新的信号。QUdpSocket的主要功能函数见表 14-6 (包括从 QAbstractSocket 继承的函数,省略了函数中的 const 关键字,省略了缺省参数)

在这里插入图片描述

在单播、广播和多播模式下,UDP 程序都是对等的,不像 TCP 通信那样分为客户端和服务器端。多播和广播的实现方式基本相同,只是数据报的目标IP 地址设置不同,多播模式需要加入多播组,实现方式有较大差异。
为分别演示这三种 UDP 通信模式,本节设计了两个实例。Samp14_3 实例演示 UDP 单播和广播通信,Samp14_4实例演示UDP组播通信。

2. UDP 单播和广播

2.1 UDP 通信实例程序功能

实例程序 samp14_3 实现 UDP 单播和广播,其主窗口是继承自 QMainWindow 的类,界面用UI 设计器设计。程序可以进行 UDP 数据报的发送和接收,samp14_3 的两个运行实例之间可以进行 UDP 通信,这两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。图 14-8和图14-9是samp14_3两个实例在一台计算机上运行时通信的界面。
在同一台计算机上运行时,两个运行实例需要绑定不同的端口,例如实例A 定端口 1200,实例B绑定端口 3355。实例A 向实例 B 发送数据报时,需要指定实例 B 所在主机的IP 地址、绑定端口作为目标地址和目标端口(为了准确识别相应的客户端,与上篇中元组的概念很相似),这样实例 B 才能接收到数据报。
如果两个实例在不同计算机上运行,则可以使用相同的端口,因为 IP 地址不同了,不会导致绑定时发生冲突。一般的UDP 通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。

2.2 主窗口类定义和构造函数

在这里插入图片描述

主窗口是基于QMainWindow 的类 MainWindow,界面采用 UI 设计器设计。MainWindow类的定义如下(省略了 UI 设计器为 actions 和按钮生成的槽函数声明):

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include    <QUdpSocket>
#include    <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *LabSocketState;//socket状态显示标签
    QUdpSocket  *udpSocket;//
    QString getLocalIP();//获取本机IP地址

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

private slots:
//自定义槽函数
    void    onSocketStateChange(QAbstractSocket::SocketState socketState);
    void    onSocketReadyRead();//读取socket传入的数据
...

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

QUdpSocket 类型的私有变量 udpSocket 是用于UDP 通信的 socket。

定义了两个自定义槽函数,onSocketStateChange()与 udpSocket 的 stateChange()信号关联,用于显示 udpSocket 当前的状态;onSocketReadyRead()信号与 udpSocket 的readyRead()信号关联,用于读取缓冲区的数据报。
MainWindow 的构造函数主要完成udpSocket 的创建、信号与槽函数的关联,代码如下:

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

    LabSocketState=new QLabel(QString::fromLocal8Bit("Socket状态:"));//
    LabSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(LabSocketState);

    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+QString::fromLocal8Bit("----本机IP:")+localIP);
    ui->comboTargetIP->addItem(localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket

    connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(udpSocket->state());

    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
}

onSocketStateChange(udpSocket->state());代码与上篇TCP通信程序里的完全一样。

2.3 UDP通信的实现

要实现UDP 数据的接收,必须先用QUdpSocket::bind()函数绑定一个端口,用于监听传入的数据报,解除绑定则使用 abort()函数。程序主窗口上的“绑定端口”和“解除绑定”按钮的响应代码如下:

void MainWindow::on_actStart_triggered()
{//绑定端口
    quint16     port=ui->spinBindPort->value(); //本机UDP端口
    if (udpSocket->bind(port))//绑定端口成功
    {
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已成功绑定"));
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定端口:")
               +QString::number(udpSocket->localPort()));

        ui->actStart->setEnabled(false);
        ui->actStop->setEnabled(true);
    }
    else
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定失败"));
}

void MainWindow::on_actStop_triggered()
{//解除绑定
    udpSocket->abort(); //不能解除绑定
    ui->actStart->setEnabled(true);
    ui->actStop->setEnabled(false);
    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已解除绑定"));
}

绑定端口后,socket 的状态变为已绑定状态“BoundState”,解除绑定后状态变为未连接状态UnconnectedState”
发送点对点消息和广播消息都使用 QUdpSocket:: writeDatagram()函数,窗口上“发送消息和“广播消息”两个按钮的代码如下:

void MainWindow::on_btnSend_clicked()
{//发送消息 按钮
    QString     targetIP=ui->comboTargetIP->currentText(); //目标IP
    QHostAddress    targetAddr(targetIP);

    quint16     targetPort=ui->spinTargetPort->value();//目标port

    QString  msg=ui->editMsg->text();//发送的消息内容

    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报

    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[out] ")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮
    quint16     targetPort=ui->spinTargetPort->value(); //目标端口
    QString  msg=ui->editMsg->text();
    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);

    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[broadcast] ")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

使用writeDatagram()函数向一个目标用户发送消息时,需要指定目标地址和端口。

在广播消息时,只需将目标地址更换为一个特殊地址,即广播地址 OHostAddress::Broadcast,一般是255.255.255.255。通信演示如下图所示:
在这里插入图片描述

QUdpSocket 发送的数据报是 QByteArray 类型的字节数组,数据报的长度一般不超过 512 字节。数据报的内容可以是文本字符串,也可以自定义格式的二进制数据,文本字符串无需以换行符结束。

QUdpSocket 接收到数据报后发射 readyRead()信号,在关联的槽函数 onSocketReadyRead()里读取缓冲区的数据报,代码如下:

void MainWindow::onSocketReadyRead()
{//读取收到的数据报
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray   datagram;
        datagram.resize(udpSocket->pendingDatagramSize());

        QHostAddress    peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
        QString str=datagram.data();

        QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

hasPendingDatagrams()表示是否有待读取的传入数据报。

pendingDatagramSize()返回待读取数据报的字节数。

readDatagram()函数用于读取数据报的内容,其函数原型为:

qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)

输入参数data和 maxSize 是必须的,表示最多读取 maxSize 字节的数据到变量 data 里。address和 port 变量是可选的,用于获取数据报来源的地址和端口。上面的代码中使用了完整的参数形式,从而可以获得数据报来源的地址 peerAddr 和端口 peerPort。如果无需获取来源地址和端口,可以采用简略形式,即:

udpSocket->readDatagram(datagram,data(),datagram,size());

读取的数据报内容是 QByteArray 字节数组,因为本程序只是传输字符串,所以简单地将其转换为字符串即可。如果传输的是自定义格式的字符串或二进制数据,需要对接收到的数据进行解析。

2.4 源码

2.4.1 可视化UI设计

在这里插入图片描述

2.4.2 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include    <QUdpSocket>
#include    <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *LabSocketState;//socket状态显示标签
    QUdpSocket  *udpSocket;//
    QString getLocalIP();//获取本机IP地址

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

private slots:
//自定义槽函数
    void    onSocketStateChange(QAbstractSocket::SocketState socketState);
    void    onSocketReadyRead();//读取socket传入的数据
//
    void on_actStart_triggered();

    void on_actStop_triggered();

    void on_actClear_triggered();

    void on_btnSend_clicked();

    void on_actHostInfo_triggered();

    void on_btnBroadcast_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

2.4.3 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include    <QtNetwork>


QString MainWindow::getLocalIP()
{
    QString hostName=QHostInfo::localHostName();//本地主机名
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);
    QString   localIP="";

    QList<QHostAddress> addList=hostInfo.addresses();//

    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            localIP=aHost.toString();
            break;
        }
    }
    return localIP;
}

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

    LabSocketState=new QLabel(QString::fromLocal8Bit("Socket状态:"));//
    LabSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(LabSocketState);

    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+QString::fromLocal8Bit("----本机IP:")+localIP);
    ui->comboTargetIP->addItem(localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket

    connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(udpSocket->state());

    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
}

MainWindow::~MainWindow()
{
    udpSocket->abort();
    delete udpSocket;
    delete ui;
}

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:UnconnectedState"));
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:HostLookupState"));
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ConnectingState"));
        break;

    case QAbstractSocket::ConnectedState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ConnectedState"));
        break;

    case QAbstractSocket::BoundState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:BoundState"));
        break;

    case QAbstractSocket::ClosingState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ClosingState"));
        break;

    case QAbstractSocket::ListeningState:
        LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ListeningState"));
    }
}

void MainWindow::onSocketReadyRead()
{//读取收到的数据报
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray   datagram;
        datagram.resize(udpSocket->pendingDatagramSize());

        QHostAddress    peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
        QString str=datagram.data();

        QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

void MainWindow::on_actStart_triggered()
{//绑定端口
    quint16     port=ui->spinBindPort->value(); //本机UDP端口
    if (udpSocket->bind(port))//绑定端口成功
    {
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已成功绑定"));
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定端口:")
               +QString::number(udpSocket->localPort()));

        ui->actStart->setEnabled(false);
        ui->actStop->setEnabled(true);
    }
    else
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定失败"));
}

void MainWindow::on_actStop_triggered()
{//解除绑定
    udpSocket->abort(); //不能解除绑定
    ui->actStart->setEnabled(true);
    ui->actStop->setEnabled(false);
    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已解除绑定"));
}

void MainWindow::on_actClear_triggered()
{
    ui->plainTextEdit->clear();
}

void MainWindow::on_btnSend_clicked()
{//发送消息 按钮
    QString     targetIP=ui->comboTargetIP->currentText(); //目标IP
    QHostAddress    targetAddr(targetIP);

    quint16     targetPort=ui->spinTargetPort->value();//目标port

    QString  msg=ui->editMsg->text();//发送的消息内容

    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报

    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[out] ")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

void MainWindow::on_actHostInfo_triggered()
{//本机地址 按钮
    QString hostName=QHostInfo::localHostName();//本地主机名
    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("本机主机名:")+hostName+"\n");
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);

    QList<QHostAddress> addList=hostInfo.addresses();//
    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            QString IP=aHost.toString();
            ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("本机IP地址:")+aHost.toString());
            if (ui->comboTargetIP->findText(IP)<0)
                ui->comboTargetIP->addItem(IP);
        }
    }

}

void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮
    quint16     targetPort=ui->spinTargetPort->value(); //目标端口
    QString  msg=ui->editMsg->text();
    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);

    ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[broadcast] ")+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

hcip的mgre和ospf实验

题目 拓扑图 一、配置环回和IP地址 R1 < Huawei>sy Enter system view, return user view with CtrlZ. [Huawei]sysname r1 [r1]int g0/0/1 [r1-GigabitEthernet0/0/1]ip add 64.1.1.1 24 Aug 4 2023 18:56:07-08:00 r1 %%01IFNET/4/LINK_STATE(l)[0]:The line protocol…

python 常见数据类型和方法

不可变数据类型 不支持直接增删改 只能查 str 字符串 int 整型 bool 布尔值 None None型特殊常量 tuple 元组(,,,)回到顶部 可变数据类型&#xff0c;支持增删改查 list 列表[,,,] dic 字典{"":"","": ,} set 集合("",""…

mysql函数及用法

目录 一、前言 二、函数 2.1五大聚合函数 2.2 日期函数 3.字符串函数 三. 总结&#xff1a; 一、前言 mySQL 是一种常见的关系型数据库管理系统&#xff0c;提供了大量的函数可以帮助开发者有效地操作和管理数据库。mySQL根据这些函数将数据更好的进行操作&#xff0c;从…

局域网部署,用WorkPlus视频会议保密又安全

用户采用私有化部署视频会议软件的情况主要有以下几种因素&#xff1a; 1. 针对机密性高的会议&#xff1a;如果有涉及高度机密的商业谈判或敏感信息交流等重要会议&#xff0c;政府、军工、企业等用户会选择局域网内部署视频会议软件&#xff0c;以保证信息安全。 2. 频繁进…

【云原生】k8s中Contrainer 生命周期回调/策略/指针学习

个人主页&#xff1a;征服bug-CSDN博客 kubernetes专栏&#xff1a;kubernetes_征服bug的博客-CSDN博客 目录 1 容器生命周期 2 容器生命周期回调/事件/钩子 3 容器重启策略 4 自定义容器启动命令 5 容器探针 1 容器生命周期 Kubernetes 会跟踪 Pod 中每个容器的状态&am…

【大数据】LPG日志采集方案(官网入门案例)

文章目录 1. LPG简介2. 安装3. 测试日志方案的效果3.1. 测试1&#xff1a;Promtail监控/var/log目录的变化3.2. 测试2&#xff1a;Grafana可视化查看日志3.3. 测试3&#xff1a;可以预见部署Spring Boot程序的日志也可以被Grafana查看3.4. 踩坑记录 4. 官方入门案例介绍4.1. 获…

收藏!9款好用的前端可视化工具推荐

“可视化开发”是上个世纪90年代软件界最大的热点之一。 当初&#xff0c;可视化开发主要专注于用户界面的构建&#xff0c;让开发者通过简单的拖拽操作&#xff0c;快速搭建用户界面&#xff0c;一些成熟产品更是实现了“所见即所得”。在与当时最先进的高级编程语言相比较时&…

基于freertos的温湿度蓝牙系统

前言&#xff1a;本项目主要是基于freertos的小项目&#xff0c;目的是为了巩固近期学习的知识&#xff0c;功能较简单&#xff0c;可自行扩充。 一、项目基本架构 项目基本功能&#xff1a;通过STM32单片机的freertos操作系统&#xff0c;将温湿度数据显示在oled屏幕上&#…

单片机复位电路分析

来分析一下这个电路&#xff1a; 首先这里面有电容&#xff0c;所以是一个动态电路。哈哈哈 假设左上角的电压源是5V的代号为VOLT。 可以知道电容capacitor C1左边的电压也是5V&#xff0c;电容中间隔着一个绝缘体&#xff0c;所以不导电&#xff0c; 这个时候电流无法通过…

【深度学习_TensorFlow】激活函数

写在前面 上篇文章我们了解到感知机使用的阶跃函数和符号函数&#xff0c;它们都是非连续&#xff0c;导数为0的函数&#xff1a; 建议回顾上篇文章&#xff0c;本篇文章将介绍神经网络中的常见激活函数&#xff0c;这些函数都是平滑可导的&#xff0c;适合于梯度下降算法。 写…

【Spring Boot】Thymeleaf模板引擎 — 表达式的语法

表达式的语法 模板的主要作用是将后台返回的数据渲染到HTML中。那么Thymeleaf是如何解析后台数据的呢&#xff1f;接下来从变量、方法、条件判断、循环、运算&#xff08;逻辑运算、布尔运算、比较运算、条件运算&#xff09;方面学习Thymeleaf表达式支持的语法。 1.赋值和拼…

eclipse Java Editor Templates

​ Window - Preferences - Java - Editor - Templates ​ date ${currentDate:date(yyyy.MM.dd)}

KubeSphere 社区双周报 | 本周六上海站 Meetup 准时开启 | 2023.7.21-08.03

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.07.21-2023.…

逆向时如何找到MingGW(GNU)编译程序的main函数

编译器是MingGW生成的可执行文件的显著特点是, 最终运行ZwContinue后程序就莫名其妙启动了, 也找不到main函数。 为了探究里面究竟怎么回事, 我找到了wrk-v1.2的源码, 其中包含了ZwContinue的实现, 首先先看一下注释, API界面包含了2个参数, 其中让人感兴趣的是PCONTEXT, 这是…

git merge 和rebase区别

Merge the incoming changes into the current branch 找到两个分支的祖先 commit&#xff0c;然后将公共分支最新版合并到自己的分支&#xff0c;形成一个新的 commit 提交&#xff0c;用图表示如下。 Rebase the current branch on top of the incoming Rebase 则是重新基于…

蓝桥杯上岸每日N题 第八期 (全球变暖)!!!

蓝桥杯上岸每日N题第八期(全球变暖)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第五期BFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &am…

JVM内存模型结构

什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一个虚构出来的计算机&#xff0c;有着自己完善的硬件架构&#xff0c;如处理器、堆栈等。 为什么需要JVM&#xff1f; Java语言使用Java虚拟机屏蔽了与具体平台相关的信息&…

9 多进程编程

文章目录 进程以及状态进程进程的状态linux下的进程管理pstopbgkillcrontab 进程的创建两个while同时运行示例获取进程pidProcess 结构给子进程指定的函数传递参数进程间是否共享全局变量补充 进程间通信-Queue常用操作Queue 实例 管道通信&#xff08;了解&#xff09;样例 进…

PDM系统的项目跟踪功效

在现代企业管理中&#xff0c;项目跟踪是一个至关重要的环节。PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;作为一款强大的数字化工具&#xff0c;为企业提供了高效的项目跟踪优势。让我们一同深入探讨&#xff0c;看看PDM系统是如何通过项…

JavaWeb 手写Tomcat底层机制

目录 一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 : 二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat : 三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …