计算机网络实验课(二)——抓取网络数据包,并实现根据条件过滤抓取的以太网帧,分析帧结构

news2025/7/19 21:28:07

文章目录

  • 一、添加控件
  • 二、代码分析
    • 2.1 代码
    • 2.2 控件初始化
    • 2.3 打开和关闭设备
    • 2.4 开始和结束捕获
    • 2.5 设置捕获条件
    • 2.6 捕获数据包
  • 三、运行程序
  • 四、结果分析

提要:如果你通过vs打开.sln文件,然后代码界面或者前端界面都没找到,视图里面也没找到的话。我这里有个小技巧(启用调试,会出现代码界面,可能是主函数的部分,需要你对Form1这个函数点进去,就是一个你的代码文件了)
在这里插入图片描述
点击中间逐语句,出现program,然后再进入Form1
在这里插入图片描述

一、添加控件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、代码分析

①利用添加的若干个控件实现捕获数据包的条件过滤
②将捕获的数据包简单分析后显示到RichTextBox控件中

2.1 代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpPcap;
using PacketDotNet;
using SharpPcap.LibPcap;
using System.Net;
using System.IO;
namespace 实验教程
{
    // 协议类型
    public enum SnapProtocol : int
    {
        udp = 0,
        tcp = 1,
        arp = 2,
        rarp = 3,
        ip = 4
    }
    

    public partial class Form1 : Form
    {
        /// <summary>
        /// 全局私有变量
        /// </summary>
        CaptureDeviceList   device_list;         // 设备列表
        ICaptureDevice      device;              // 当前选择设备
        DelegateMethod      disp_info;           // 委托

        

        public Form1()
        {
            InitializeComponent();
            
            // 获得设备列表
            device_list = GetDeviceList();
            
            // 获取支持的协议列表
            LoadProto();

            // 载入所有网卡
            LoadDevice();

            // 显示数据包委托函数
            disp_info = new DelegateMethod(Disp_PacketInfo);
        }

        #region 程序初始化

        /// <summary>
        /// 获得当前的设备列表(网卡)
        /// </summary>
        /// <returns></returns>
        private CaptureDeviceList GetDeviceList()
        {
            // Print SharpPcap version 
            string ver = SharpPcap.Version.VersionString;
            this.richTextBox1.Text = string.Format("SharpPcap {0}, Device List\n", ver);
            try
            {
                // Retrieve the device list
                CaptureDeviceList devices = CaptureDeviceList.Instance;

                // If no devices were found print an error
                if (devices.Count < 1)
                {
                    this.richTextBox1.Text += "No devices were found on this machine\n";
                    return null;
                }

                this.richTextBox1.Text += "\nThe following devices are available on this machine:\n";
                this.richTextBox1.Text += "----------------------------------------------------\n";

                // Print out the available network devices
                foreach (ICaptureDevice dev in devices)
                    this.richTextBox1.Text += string.Format("{0}\n", dev.ToString());

                return devices;
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return null;
            }
        }

        /// <summary>
        /// 获取支持的协议列表
        /// </summary>
        private void LoadProto()
        {
            comboBox2.Items.Clear();
            foreach (string e in Enum.GetNames(typeof(SnapProtocol)))
            {
                comboBox2.Items.Add(e);
            }
            comboBox2.SelectedIndex = 0;
        }

        /// <summary>
        /// 载入所有网卡信息
        /// </summary>
        private void LoadDevice()
        {
            comboBox1.Items.Clear();
            if (device_list == null)
            {
                MessageBox.Show("没有找到任何网卡设备!");
                return;
            }

            foreach (LibPcapLiveDevice dev in device_list)
            {
                try
                {
                    comboBox1.Items.Add(dev.Addresses[0].Addr);
                }
                catch
                {
                    continue;
                }
            }
            comboBox1.SelectedIndex = 0;
        }

        #endregion

        /// <summary>
        /// 打开设备
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {

            if (button1.Text == "打开设备")
            {
                button1.Text = "关闭设备";
                groupBox1.Enabled = false;


                device = device_list[comboBox1.SelectedIndex];
                device.OnPacketArrival += new SharpPcap.PacketArrivalEventHandler(device_OnPacketArrival);
                device.Open(DeviceMode.Promiscuous, 1000);
                device.Filter = RcvPacketFilter();
                device.StartCapture();
            }
            else
            {
                button1.Text = "打开设备";
                groupBox1.Enabled = true;


                try {
                    device.StopCapture();
                }
                catch { device.Close();
                }
                
            }
            string result1 = @"C:\Users\19468\Desktop\\测试文件.txt";//结果保存到桌面
            FileStream fs = new FileStream(result1, FileMode.Append);
            StreamWriter wr = null;
            wr = new StreamWriter(fs);
            wr.WriteLine("测试!");
            wr.Close();
        }

        #region 捕获和显示函数

        /// <summary>
        /// 根据要求构造数据包过滤字符串
        /// </summary>
        private string RcvPacketFilter()
        {
            SnapProtocol proto = (SnapProtocol)comboBox2.SelectedIndex;
            IPAddress src_host, dst_host;

            string filter = proto.ToString();
            if ((textBox1.Text.Trim().Length > 0) && (IPAddress.TryParse(textBox1.Text, out src_host)))
            {
                filter += string.Format(" and src host {0}", src_host.ToString());
            }

            if ((textBox2.Text.Trim().Length > 0) && (IPAddress.TryParse(textBox2.Text, out dst_host)))
            {
                filter += string.Format(" and dst host {0}", dst_host.ToString());
            }
        
            return filter;
        }

        /// <summary>
        /// 抓包事件函数,在抓到符合条件的数据包的时候该函数将被调用
        /// 功能:
        ///     1. 获得当前数据包的时间间隔、长度、协议类型、地址等参数
        ///     2. 将信息输出到RichTextBox控件显示出来
        /// </summary>
        private void device_OnPacketArrival(object sender, CaptureEventArgs packet)
        {
            // 时间和长度的获取
            DateTime time = packet.Packet.Timeval.Date;
            int len = packet.Packet.Data.Length;
            // 解析数据包成:IP包
            Packet p = Packet.ParsePacket(packet.Packet.LinkLayerType, packet.Packet.Data);
            IpPacket ip = (IpPacket)p.Extract(typeof(IpPacket));

            // 数据包信息
            string info = string.Format("\nsrc_addr={0}, des_addr={1}, type={2}\n",
                ip.SourceAddress, ip.DestinationAddress, ip.Protocol);
            info += string.Format("{0}:{1}:{2},{3} Len={4}\n",
                time.Hour, time.Minute, time.Second, time.Millisecond, len);
            info += string.Format(byteToHexStr(packet.Packet.Data));

            // 使用委托显示结果
            richTextBox1.Invoke(disp_info, info);
        }


        delegate void DelegateMethod(string info);
        /// <summary>
        /// 显示收到数据包的信息(由于捕获过程开辟了新线程,因此捕获结果需要委托来传递到RichTextBox)
        /// </summary>
        /// <param name="info"></param>
        private void Disp_PacketInfo(string info)
        {
            richTextBox1.Text += info;
        }

        #endregion

        #region 字符串与byte数组相互转换

        /// <summary> 
        /// 字节数组转16进制字符串 
        /// </summary> 
        /// <param name="bytes"></param> 
        /// <returns></returns> 
        public static string byteToHexStr(byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2") + " ";
                }
            }
            return returnStr;
        }

        /// <summary> 
        /// 字符串转16进制字节数组 
        /// </summary> 
        /// <param name="hexString"></param> 
        /// <returns></returns> 
        public static byte[] strToToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        /// <summary>
        /// 字符串IP地址转换成byte数组
        /// </summary>
        /// <param name="decString"></param>
        /// <returns></returns>
        public static byte[] strIPToByte(string decString)
        {
            string[] decStringArray = decString.Split('.');
            if (decStringArray.Length == 4)
            {
                byte[] returnBytes = new byte[4];
                for (int i = 0; i < 4; i++)
                {
                    returnBytes[i] = Convert.ToByte(decStringArray[i]);
                }
                return returnBytes;
            }
            else
            {
                MessageBox.Show("IP地址格式错误!(参考:192.168.0.1)");
                return null;
            }
        }

        #endregion
        

    }
}

2.2 控件初始化

捕获设置中使用ComboBox选择网卡和协议类型。
其中,协议类型定义如下

// 协议类型
public enum SnapProtocol : int 
{ 
udp = 0, 
tcp = 1, 
arp = 2, 
rarp = 3, 
ip = 4 
} 

网卡信息则由实验一中的函数获得。现将这些选项填充到 ComboBox 中,这里使用两个函数来完成这两个ComboBox的初始化,即填充Item属性。函数的具体代码如下:

        /// <summary>
        /// 获取支持的协议列表
        /// </summary>
        private void LoadProto()
        {
            comboBox2.Items.Clear();
            foreach (string e in Enum.GetNames(typeof(SnapProtocol)))
            {
                comboBox2.Items.Add(e);
            }
            comboBox2.SelectedIndex = 0;
        }

        /// <summary>
        /// 载入所有网卡信息
        /// </summary>
        private void LoadDevice()
        {
            comboBox1.Items.Clear();
            if (device_list == null)
            {
                MessageBox.Show("没有找到任何网卡设备!");
                return;
            }

            foreach (LibPcapLiveDevice dev in device_list)
            {
                try
                {
                    comboBox1.Items.Add(dev.Addresses[0].Addr);
                }
                catch
                {
                    continue;
                }
            }
            comboBox1.SelectedIndex = 0;
        }

这些内容是在程序一开始执行,因此同实验一中获得设备列表一样,这两个函数可以放在界面初始化以后执行,紧随GetDeviceList函数:

      public Form1()
        {
            InitializeComponent();
            
            // 获得设备列表
            device_list = GetDeviceList();
            
            // 获取支持的协议列表
            LoadProto();

            // 载入所有网卡
            LoadDevice();

            // 显示数据包委托函数
            disp_info = new DelegateMethod(Disp_PacketInfo);
        }

至此,控件的初始化就完成了,下面是实现功能的关键步骤。

2.3 打开和关闭设备

打开设备调用ICaptureDevice对象的Open函数实现,ICaptureDevice 对象则通过实验一获得的设备列表得到,方式如下:

ICaptureDevice device = device_list[i];

Open 函数有三个重载:
Open()
Open(DeviceMode mode)
Open(DeviceMode mode, int read_timeout)

mode 参数可选DeviceMode.NormalDeviceMode.Promiscuous,分别代表普通模式和混合模式。其中普通模式捕获到达本机或者从本机发出的数据包,忽略转发到其他终端的数据包;而混合模式将捕获所有到达网卡的数据包。
timeout 参数指示超时时间,单位为毫秒,设置为0表示不限制超时。

关闭设备使用Close函数即可。

在本项目中,打开和关闭由按钮控制,因此该按钮(button1)的Click事件函数编辑如下,特别注意黄色高亮部分:
在这里插入图片描述

        private void button1_Click(object sender, EventArgs e)
        {

            if (button1.Text == "打开设备")
            {
                button1.Text = "关闭设备";
                groupBox1.Enabled = false;


                device = device_list[comboBox1.SelectedIndex];
                device.OnPacketArrival += new SharpPcap.PacketArrivalEventHandler(device_OnPacketArrival);
                device.Open(DeviceMode.Promiscuous, 1000);
                device.Filter = RcvPacketFilter();
                device.StartCapture();
            }
            else
            {
                button1.Text = "打开设备";
                groupBox1.Enabled = true;


                try {
                    device.StopCapture();
                }
                catch { device.Close();
                }
                
            }

2.4 开始和结束捕获

在打开设备以后,捕获还未真正开始,捕获操作将调用专门的捕获函数后开始。捕获分为异步捕获和同步捕获,异步捕获需要注册事件 OnPacketArrival;而同步捕获只需要在打开设备以后调用函数 GetNextPacket,并且为了防止阻塞主线程,同步捕获通常需要开辟一个线程去实现。同步捕获方式参照下方链接文章说明:
SharpPcap - A Packet Capture Framework for .NET
本例程使用异步方式,基本流程为:
注册OnPacketArrival 事件——》StartCapture——》StopCapture。使用方式见上一节button1的Click事件灰色背景部分代码。

在StartCapture 执行后,一旦有符合条件的网络数据包到达网卡,被注册的事件函数(device_OnPacketArrival)就会执行,这一过程类似Button的Click事件的执行过程。

2.5 设置捕获条件

不对捕获的包进行条件过滤将捕获所有到达网卡的数据包是没有意义的。通常对网络封包进行分析需要能够根据封包的参数对收到的数据包进行过滤,从而快速定位到想要进行分析的封包。成熟的网络封包分析软件能够依据 IP、协议类型、端口、数据大小、通讯方向、数据内容等等一系列参数对封包进行过滤。 在SharpPcap 中,参数的过滤通过设置字符串表达式来实现,即,通过设置ICaptureDevice 对象的 Filter 属性来实现,例如:
string filter = "ip and tcp";
device.Filter = filter;
上述表达式表示只接收TCP/IP数据包。
过滤字符串的格式完全参照 WinPcap 用户手册,详细参数的定义和解释见下方链接:
Filtering expression syntax [WinPcap user’s manual]
在本教程中,展示了如何对协议类型、源IP地址和目的IP地址这三个参数进行过滤。如下方代码中高亮所示,在开始捕获之前设置Filter属性实现封包过滤,其中RcvPacketFilter 函数用于生产过滤字符串表达式。
在这里插入图片描述
RcvPacketFilter 函数负责对控件中输入的参数进行判断,然后生成规定格式的字符串,代码如下:

        /// <summary>
        /// 根据要求构造数据包过滤字符串
        /// </summary>
        private string RcvPacketFilter()
        {
            SnapProtocol proto = (SnapProtocol)comboBox2.SelectedIndex;
            IPAddress src_host, dst_host;

            string filter = proto.ToString();
            if ((textBox1.Text.Trim().Length > 0) && (IPAddress.TryParse(textBox1.Text, out src_host)))
            {
                filter += string.Format(" and src host {0}", src_host.ToString());
            }

            if ((textBox2.Text.Trim().Length > 0) && (IPAddress.TryParse(textBox2.Text, out dst_host)))
            {
                filter += string.Format(" and dst host {0}", dst_host.ToString());
            }
        
            return filter;
        }

表达式最终的格式为:
proto{ and src host xxx.xxx.xxx.xxx}{ and dst host xxx.xxx.xxx.xxx}
如果TextBox 控件中填入了IP地址,则对于源IP地址(src host)或者目的IP 地址(dst host)就作为过滤条件,例如:
udp and src host 192.168.1.100 and dst host 192.168.1.101
如果对应的IP地址为空,该项就被忽略。例如,源地址TextBox为空,则最终生成的字符串为:
udp and dst host 192.168.1.101
按照上述方式构造字符串即可实现过滤,其它过滤条件使用方式类似。

2.6 捕获数据包

在设置完成后,进入捕获状态后,如4.3.3节所述,捕获到满足条件的数据包以后,程序将调用被注册的事件函数 device_OnPacketArrival,通过该函数我们可以通过编写相关代码对捕获的数据包进行处理。
本实验展示了如何在 OnPacketArrival 中提取数据包的时间戳、长度、源IP 地址、目的IP地址以及协议类型。上述参数将伴随整个数据包内容以字符串的形式显示在RichTextBox中。函数定义如下:
在这里插入图片描述

        private void device_OnPacketArrival(object sender, CaptureEventArgs packet)
        {
            // 时间和长度的获取
            DateTime time = packet.Packet.Timeval.Date;
            int len = packet.Packet.Data.Length;
            // 解析数据包成:IP包
            Packet p = Packet.ParsePacket(packet.Packet.LinkLayerType, packet.Packet.Data);
            IpPacket ip = (IpPacket)p.Extract(typeof(IpPacket));

            // 数据包信息
            string info = string.Format("\nsrc_addr={0}, des_addr={1}, type={2}\n",
                ip.SourceAddress, ip.DestinationAddress, ip.Protocol);
            info += string.Format("{0}:{1}:{2},{3} Len={4}\n",
                time.Hour, time.Minute, time.Second, time.Millisecond, len);
            info += string.Format(byteToHexStr(packet.Packet.Data));

            // 使用委托显示结果
            richTextBox1.Invoke(disp_info, info);
        }

在这一节中需要对以太网的帧格式有一定的了解,本试验中部分参数需要解析数据包的内容来获得。其中程序的时间和数据长度可以直接访问属性获得,而以太网帧内的IP地址信息则需要通过分析数据包内容得到。

SharpPcap 中提供了所有报文格式的解析方式,可以方便直观地解析以太网中所有的字段,如上述代码中高亮部分:

函数Packet.ParsePacket 用于将数据 packet.Packet.Data 解析成以太网帧;Extract 函数可以提取并解析以太网帧中的各种报文。进行提取和解析后,其中的组成部分就可以通过属性的形式获得。如上述代码中的灰色部分,IP 数据报的源IP地址、目的IP地址和协议类型通过属性的形式可以直接获得。 最后,将所有内容转换成字符串,提交给委托函数进行显示。

三、运行程序

在这里插入图片描述

四、结果分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

78. Subsets和90. Subsets II

目录 78.子集 方法一、迭代法实现子集枚举 方法二、递归法实现子集枚举 方法三、根据子集元素个数分情况收集 方法四、直接回溯法 90.子集二 方法一、迭代法实现子集枚举 方法二、递归法实现子集枚举 方法三、根据子集元素个数分情况收集 方法四、直接回溯法 78.子集…

ElasticSearch整合SpringBoot

ElasticSearch 整合SpringBoot ES官方提供了各种不同语言的客户端。用来操作ES。这些客户端的本质就是组装DSL语句&#xff0c;通过HTTP请求发送给ES。 设计索引库 跟据数据库的表结构进行ES索引库的创建时。如果字段需要进行倒排索引的时候请为它指定分词器。如果该字段不是…

2025上半年软考高级系统架构设计师经验分享

笔者背景 笔者在成都工作近7年&#xff0c; 一直担任研发大头兵&#xff0c;平日工作主要涵盖应用开发&#xff08;Java&#xff09;与数仓开发&#xff0c;对主流数据库、框架等均有涉猎&#xff0c;但谈不上精通。 最近有一些职业上的想法&#xff0c;了解到软考有那么一丁点…

uni-app学习笔记十二-vue3中创建组件

通过组件&#xff0c;可以很方便地实现页面复用&#xff0c;减少重复页面的创建&#xff0c;减少重复代码。一个页面可以引入多个组件。下面介绍在HBuilder X中创建组件的方法&#xff1a; 一.组件的创建 1.选中项目&#xff0c;右键-->新建目录(文件夹)&#xff0c;并将文…

一键启动多个 Chrome 实例并自动清理的 Bash 脚本分享!

目录 一、&#x1f4e6; 脚本功能概览 二、&#x1f4dc; 脚本代码一览 三、&#x1f50d; 脚本功能说明 &#xff08;一&#xff09;✅ 支持批量启动多个 Chrome 实例 &#xff08;二&#xff09;✅ 每个实例使用独立用户数据目录 &#xff08;三&#xff09;✅ 启动后自…

4 月 62100 款 App 被谷歌下架!环比增长 28%

大家好&#xff0c;我是牢鹅&#xff01;上周刚刚结束的 2025 年 Google I/O 开发者大会&#xff0c; Google Play 带来了一系列的更新&#xff0c;主要围绕提升优质 App 的"发现"、"互动"和"收入"三大核心内容。 这或许正是谷歌生态的一个侧影…

mediapipe标注视频姿态关键点(基础版加进阶版)

前言 手语视频流的识别有两种大的分类&#xff0c;一种是直接将视频输入进网络&#xff0c;一种是识别了关键点之后再进入网络。所以这篇文章我就要来讲讲如何用mediapipe对手语视频进行关键点标注。 代码 需要直接使用代码的&#xff0c;我就放这里了。环境自己配置一下吧&…

PCtoLCD2002如何制作6*8字符

如何不把“等比缩放”前的打勾取消&#xff0c;则无法修改为对应英文字符为6*8。 取消之后就可以更改了&#xff01;

SmartPlayer与VLC播放RTMP:深度对比分析延迟、稳定性与功能

随着音视频直播技术的发展&#xff0c;RTMP&#xff08;实时消息传输协议&#xff09;成为了广泛应用于实时直播、在线教育、视频会议等领域的重要协议。为了确保优质的观看体验&#xff0c;RTMP播放器的选择至关重要。大牛直播SDK的SmartPlayer和VLC都是在行业中广受欢迎的播放…

Qt QPaintEvent绘图事件painter使用指南

绘制需在paintEvent函数中实现 用图片形象理解 如果加了刷子再用笔就相当于用笔画过的区域用刷子走 防雷达&#xff1a; 源文件 #include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QPainter> Widget::Widget(QWidget…

伪创新-《软件方法》全流程引领AI-第1章 04

《软件方法》全流程引领AI-第1章 ABCD工作流-01 对PlantUML们的评价-《软件方法》全流程引领AI-第1章 02 AI辅助的建模步骤-《软件方法》全流程引领AI-第1章 03 第1章 ABCD工作流 1.5 警惕和揭秘伪创新 初中数学里要学习全等三角形、相似三角形、SSS、SAS……&#xff0c;到…

【iOS】 锁

iOS 锁 文章目录 iOS 锁前言线程安全锁互斥锁pthread_mutexsynchronized (互斥递归锁)synchronized问题:小结 NSLockNSRecursiveLockNSConditionNSConditionLock 自旋锁OSSpinLock(已弃用)atomicatomic修饰的属性绝对安全吗?os_unfair_lock 读写锁互斥锁和自旋锁的对比 小结使…

uni-app学习笔记十五-vue3页面生命周期(一)

页面生命周期概览 vue3页面生命周期如下图所示&#xff1a; onLoad 此时页面还未显示&#xff0c;没有开始进入的转场动画&#xff0c;页面dom还不存在。 所以这里不能直接操作dom&#xff08;可以修改data&#xff0c;因为vue框架会等待dom准备后再更新界面&#xff09;&am…

《软件工程》第 14 章 - 持续集成

在软件工程的开发流程中&#xff0c;持续集成是保障代码质量与开发效率的关键环节。本章将围绕持续集成的各个方面展开详细讲解&#xff0c;结合 Java 代码示例与可视化图表&#xff0c;帮助读者深入理解并实践相关知识。 14.1 持续集成概述 14.1.1 持续集成的相关概念 持续集…

Orpheus-TTS:AI文本转语音,免费好用的TTS系统

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、Orpheus-TTS&#xff1a;重新定义语音合成的标准1. 什么是Orpheus-TTS&#xff…

STM32 Keil工程搭建 (手动搭建)流程 2025年5月27日07:42:09

STM32 Keil工程搭建 (手动搭建)流程 觉得麻烦跳转到最底部看总配置图 1.获取官方标准外设函数库 内部结构如下: 文件夹功能分别为 图标(用不上)库函数(重点) Libraries/ ├── CMSIS/ # ARM Cortex-M Microcontroller Software Interface Standard…

OpenGL Chan视频学习-7 Writing a Shader inOpenGL

bilibili视频链接&#xff1a; 【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p5&vd_source44b77bde056381262ee55e448b9b1973 函数网站&#xff1a; docs.gl 说明&#xff1a; 1.之后就不再整理具体函数了&#xff0c;网站直接翻译会更直观也会…

顶会新方向:卡尔曼滤波+目标检测

卡尔曼虑波&#xff0b;目标检测创新结合&#xff0c;新作准确率突破100%! 一个有前景且好发论文的方向:卡尔曼滤波&#xff0b;目标检测! 这种创新结合&#xff0c;得到学术界的广泛认可&#xff0c;多篇成果陆续登上顶会顶刊。例如无人机竞速系统 Swift&#xff0c;登上nat…

一起学数据结构和算法(二)| 数组(线性结构)

数组&#xff08;Array&#xff09; 数组是最基础的数据结构&#xff0c;在内存中连续存储&#xff0c;支持随机访问。适用于需要频繁按索引访问元素的场景。 简介 数组是一种线性结构&#xff0c;将相同类型的元素存储在连续的内存空间中。每个元素通过其索引值&#xff08;数…

Linux基本指令篇 —— touch指令

touch是Linux和Unix系统中一个非常基础但实用的命令&#xff0c;主要用于操作文件的时间戳和创建空文件。下面我将详细介绍这个命令的用法和功能。 目录 一、基本功能 1. 创建空文件 2. 同时创建多个文件 3. 创建带有空格的文件名&#xff08;需要使用引号&#xff09; 二、…