C# 事件

目录

    • 1、事件模型的5个组成部分
    • 2、使用内置委托类型声明事件
      • 2.1 `EventHandler`
        • 2.1.1 ?
        • 2.1.2 this
        • 2.1.3 使用匿名函数和lamda表达式
          • 2.1.3.1 匿名函数
          • 2.1.3.2 lamda表达式
        • 2.1.4 异常处理
      • 2.2 `EventHandler<TEventArgs>`
    • 3、使用自定义委托类型声明事件
      • 3.1 事件的完整声明
      • 3.2 事件的简化声明
    • 4、委托和事件的区别
    • 5、事件的作用

事件(event)让类或对象具备在某件感兴趣的事发生时通知其他类或对象的能力,触发事件的类(让事件发生的类)叫做发布者(publisher),处理事件的类叫做订阅者(subscriber)。
在这里插入图片描述
举个例子,闻鸡起舞,这个成语中有两个对象,鸡和祖逖。凌晨1-3点,鸡开始第一次的啼叫,祖逖听到鸡叫声立马起床开始舞剑。鸡是事件的发布者,当时间到了凌晨1-3点时,鸡叫事件发生了,而祖逖的行为和鸡叫这个事件之间存在着订阅关系(祖逖在心里告诉自己,只要我听到鸡叫声了,就赶紧起床舞剑)。因此,鸡叫时间一发生,就会向祖逖发出通知信号,祖逖接收到了信号,就会采取相应的行动。

需要注意的是:

  • 1.祖逖的行为和鸡叫这个事件之间需要有订阅关系。也就是说,因为有订阅关系,所以鸡叫的发生才会让祖逖舞剑。而如果是狗叫,祖逖并不会因此舞剑。
  • 2.一个发布者–>多个订阅者。不光是祖逖听到鸡叫会起床舞剑,邻居家听到鸡叫也可以采取相应行动,比如赶集。
  • 3.多个发布者–>一个订阅者。祖逖不光听到自家的鸡叫起床舞剑,听到别人家的鸡叫也会起床舞剑。

在这里插入图片描述

1、事件模型的5个组成部分

  • 事件的拥有者(event source)。也就是Publisher。
  • 事件成员(event)。事件是隶属于发布者的,比如公司上市这个事件是隶属于公司主体的,没有公司何谈公司上市?
  • 事件的订阅者(响应者)。也就是Subscriber。
  • 事件处理器(event handler)。是订阅者的一个成员函数,本质上是一个回调函数。所谓函数,就是用来执行特定功能的代码块;而回调函数,就是在程序需要的时候会去调用,不需要的时候不去调用的函数。当Publisher没有向Subscriber发通知时,回调函数不会执行,一旦Publisher向Subscriber发通知,回调函数立马执行。
  • 事件订阅(subscribe)。将事件和事件处理器联系起来,本质上是一种以委托类型为基础的约定。

2、使用内置委托类型声明事件

.net framework中提供了内置委托类型EventHandlerEventHandler<TEventArgs>EventHandler用来声明不包含事件参数的事件,EventHandler<TEventArgs>用来声明包含事件参数的事件。事件参数是事件发生时,发布者会向订阅者发送的数据。比如闻鸡起舞这个事件的发生,鸡并没有向祖逖发送任何信息,发送了也听不懂哈哈,因为鸡叫本身已经能说明所有问题,不需要额外的信息了。再比如手机收到信息了,手机铃声响这个事件会向人发送通知的同时还会发送信息(事件参数),铃声只是提醒人看手机,信息才是关键,所以在这个事件中需要有事件参数。

2.1 EventHandler

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            People zuDi = new People();
            chicken.Crow += zuDi.Action;
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;

        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this,e);
        }
    }

    class People
    {
        public void Action(object sender, EventArgs e)
        {
            Console.WriteLine("I get up.");
            Console.WriteLine("I started practicing my sword.");
        }
    }
}

/*
Outputs:
koke-kokko
I get up.
I started practicing my sword.
*/

这个例子是对闻鸡起舞的实现,Chicken类是对发布者的实现,包含使用event关键字声明的内置委托类型的事件,以及触发事件。

  • +=:建立事件和事件处理器之间的订阅关系。
  • -=:取消事件和事件处理器之间的订阅关系。

为什么要取消事件和事件处理器之间的订阅关系?防止内存泄漏。事件订阅者持有对事件发布者的引用,当订阅者订阅了一个事件时,事件发布者会将订阅者添加到其事件订阅者列表中。这意味着,只要事件发布者存在,订阅者对象就不会被垃圾回收器回收,即使订阅者对象本身不再被其他部分的代码引用。
在这里插入图片描述

比如,在这样一个软件中,有很多可以点击的按钮,每个按钮点击后会产生不同的效果。整个窗体就是一个发布者,而每个按钮是一个订阅者,如果我们每次点击完按钮产生相应效果后,不取消按钮和窗体之间的订阅关系,那么随着我们点击的按钮越来越多,内存中需要存储越来越多这样的订阅关系,而这种订阅关系并不会自动清除掉,除非我们将软件关闭,也就是发布者被干掉了。因此不及时取消订阅很容易导致内存泄漏。

Crow?.Invoke(this,e);

着重说一下这行代码,为什么要加?,以及传入的参数this有什么用?

2.1.1 ?

这里的?是可空类型修饰符,考虑一种情况:将事件处理器订阅事件的这行代码注释掉,然后将可空类型修饰符删掉,这个时候再运行代码。

在这里插入图片描述

在这里插入图片描述

会报错,“未将对象引用设置到对象的实例”,我们知道,Invoke方法是委托用来间接调用函数的(C# 委托),由于我们将chicken.Crow += zuDi.Action;注释掉了,也就是说,委托是null,没有引用到任何实例对象,就会报错了。当没有任何的事件处理器订阅事件时,处于代码的健壮性考虑,我们会加上可空类型修饰符,避免出现这种报错。通过使用?,C#在调用Invoke方法之前,会先检查Crow是否为空,如果为空,就不会去调用Invoke方法,从而避免了空引用异常。

在这里插入图片描述我们也可以显式地对Crow是否为空进行判断。

if(Crow != null)
{
	Crow.Invoke(this, e);
}
2.1.2 this
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            chicken.Name = "BigChicken";
            People zuDi = new People();
            chicken.Crow += zuDi.Action;
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;
        public string Name { get; set; }
        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this, e);
        }
    }

    class People
    {
        public void Action(object sender, EventArgs e)
        {
            if(sender != null)
            {
                Chicken chicken = sender as Chicken;
                Console.WriteLine("I hear the {0} is crowing.",chicken.Name);
                Console.WriteLine("I get up.");
                Console.WriteLine("I started practicing my sword.");
            }
        }
    }
}

/*
Outputs:
koke-kokko
I hear the BigChicken is crowing.
I get up.
I started practicing my sword.
*/

this代表的是Chicken的实例。假设这样一种情况,祖逖现在只有在听到BigChicken这一只鸡叫,才会起床舞剑,也就是说,任何其他的鸡叫,祖逖都不会做出反应,那么这个时候,就需要能在事件处理器中判断是哪一个对象触发的事件。sender是对this的引用。
在这里插入图片描述

2.1.3 使用匿名函数和lamda表达式
2.1.3.1 匿名函数

为了使程序更简洁,我们可以使用匿名函数或lamda表达式来完成订阅者的功能。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            chicken.Crow += delegate (object sender, EventArgs e)
            {
                Console.WriteLine("I get up.");
                Console.WriteLine("I started practicing my sword.");
            };
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;

        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this, e);
        }
    }
}
2.1.3.2 lamda表达式
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Chicken chicken = new Chicken();
            chicken.Crow += (sneder, e) =>
            {
                Console.WriteLine("I get up.");
                Console.WriteLine("I started practicing my sword.");
            };
            chicken.StartCrow();
            Console.ReadLine();
        }
    }

    class Chicken
    {
        public event EventHandler Crow;

        public void StartCrow()
        {
            Console.WriteLine("koke-kokko");
            OnCrow(EventArgs.Empty);
        }

        protected virtual void OnCrow(EventArgs e)
        {
            Crow?.Invoke(this, e);
        }
    }
}
2.1.4 异常处理
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Bell bell = new Bell();
            Teather teather = new Teather();
            Student student = new Student();
            bell.Ring += teather.Action;
            bell.Ring += student.Action;
            bell.BellRing();
            Console.ReadLine();
        }
    }

    class Bell
    {
        public event EventHandler Ring;

        public void BellRing()
        {
            Console.WriteLine("Ding!Dang!");
            OnRing(EventArgs.Empty);
        }

        protected virtual void OnRing(EventArgs e)
        {
            Ring?.Invoke(this,e);
        }
    }

    class Teather
    {
        public void Action(object sender, EventArgs e)
        {
            Console.WriteLine("I walked into the classroom.");
        }
    }

    class Student
    {
        public void Action(object sender, EventArgs e)
        {
            Console.WriteLine("I settled into my seat.");
        }
    }
}

/*Outputs:
Ding!Dang!
I walked into the classroom.
I settled into my seat.
*/

上课铃声响起,老师走进教室,学生在座位上坐好。这个例子中一个事件的发布者对应着两个订阅者。如果老师这个订阅者发生异常,我们来看看程序的运行结果。
在这里插入图片描述
程序直接抛出异常,这就意味着,不仅老师的事件处理函数无法执行,学生的事件处理函数也无法执行。
当多个订阅者订阅发布者的消息时,一个订阅者出现错误,会导致后面所有的订阅者都无法接收到消息,因此为了避免这种情况的发生,需要进行异常处理。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Bell bell = new Bell();
            Teather teather = new Teather();
            Student student = new Student();
            bell.Ring += teather.Action;
            bell.Ring += student.Action;
            bell.BellRing();
            Console.ReadLine();
        }
    }

    class Bell
    {
        public event EventHandler Ring;

        public void BellRing()
        {
            Console.WriteLine("Ding!Dang!");
            OnRing(EventArgs.Empty);
        }

        protected virtual void OnRing(EventArgs e)
        {
            Ring?.Invoke(this, e);
        }
    }

    class Teather
    {
        public void Action(object sender, EventArgs e)
        {
            try
            {
                //Console.WriteLine("I walked into the classroom.");
                throw new Exception("Teacher throw an exception.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex.Message);
            }
        }
    }

    class Student
    {
        public void Action(object sender, EventArgs e)
        {
            try
            {
                Console.WriteLine("I settled into my seat.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex.Message);
            }
        }
    }
}

/*Outputs:
Ding!Dang!
Exception caught: Teacher throw an exception.
I settled into my seat.
*/

这样,哪怕老师的事件处理函数抛出异常,也不会影响学生的事件处理函数正常执行。

2.2 EventHandler<TEventArgs>

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Phone phone = new Phone();
            People people = new People();
            phone.MessageReceived += people.Action;
            phone.ReceiveMessage();
            Console.ReadLine();
        }
    }

    class MessageReceivedEventArgs:EventArgs
    {
        public string Message { get; }
        public MessageReceivedEventArgs(string message)
        {
            Message = message;
        }
    }

    class Phone
    {
        public event EventHandler<MessageReceivedEventArgs> MessageReceived;
        public void ReceiveMessage()
        {
            Console.WriteLine("Ding,Ding,Ding.");
            MessageReceivedEventArgs message = new MessageReceivedEventArgs("Hello,World!");
            OnMessageReceived(message);
        }

        protected virtual void OnMessageReceived(MessageReceivedEventArgs e)
        {
            MessageReceived?.Invoke(this, e);
        }
    }

    class People
    {
        public void Action(object sender, MessageReceivedEventArgs e)
        {
            string message = e.Message;
            Console.WriteLine("I receive the mesage.");
            Console.WriteLine("The message is: {0}", message);
        }
    }
}

/*Outputs:
Ding,Ding,Ding.
I receive the mesage.
The message is: Hello,World!
*/

3、使用自定义委托类型声明事件

3.1 事件的完整声明

namespace ConsoleApp1
{
    public delegate void DingEventHandler(Phone phone, DingEventArgs e);

    public class DingEventArgs:EventArgs
    {
        public string Message { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Phone phone = new Phone();
            People people = new People();
            phone.Ding += new DingEventHandler(people.Action);
            DingEventArgs e = new DingEventArgs();
            e.Message = "Hello,World!";
            phone.MessageReceived(e);
            Console.ReadLine();
        }
    }

    public class Phone
    {
        private DingEventHandler dingEventHandler;
        public string ID { get; }
        public Phone()
        {
            ID = "123456";
        }
        public event DingEventHandler Ding
        {
            add
            {
                this.dingEventHandler += value;
            }
            remove
            {
                this.dingEventHandler -= value;
            }
        }

        public void MessageReceived(DingEventArgs e)
        {
            Console.WriteLine("Ding,Ding!");
            Console.WriteLine("Here comes the message.");
            OnDing(e);
        }

        protected virtual void OnDing(DingEventArgs e)
        {
            this.dingEventHandler?.Invoke(this, e);
        }
    }

    public class People
    {
        public void Action(Phone phone, DingEventArgs e)
        {
            string id = phone.ID;
            string message = e.Message;
            Console.WriteLine("I receive message: [{0}], from {1}",message,id);
        }
    }
}

/*Outputs:
Ding,Ding!
Here comes the message.
I receive message: [Hello,World!], from 123456
*/

以上是事件声明的完整形式,它能够让我们理解事件运行的本质原理。都说事件是基于委托的,那委托究竟在事件中是怎么发挥作用的呢?
我们知道,委托相当于C++中的函数指针,也就是对函数的引用,我们可以通过调用委托实例的方法,来间接调用委托所引用的函数,而不是直接去调用函数,这样做的好处是,可以将函数封装在委托中,把函数以参数的形式进行传递,也可以将函数赋值给一个变量。参见C# 委托。

public delegate void DingEventHandler(Phone phone, DingEventArgs e);

这行代码定义了一个委托类型。

private DingEventHandler dingEventHandler;

这行代码定义了一个委托类型的私有字段。

public event DingEventHandler Ding
{
	add
    {
    	this.dingEventHandler += value;
    }
    remove
    {
    	this.dingEventHandler -= value;
    }
}

这段代码是对事件的定义,使用event关键字定义一个委托类型的事件。通过addremove访问器,来将委托和函数绑定在一起,以及取消绑定,value是上下文关键字,代表传进来的函数(方法)。

// this.dingEventHandler += value;
this.dingEventHandler += new DingEventHandler(value);

其实,下面的代码才是完整的表达方式,这一步的过程是:将value所代表的函数封装到DingEventHandler委托中。
在这里插入图片描述

this.dingEventHandler?.Invoke(this, e);

这一行代码是通过委托实例的Invoke方法,来间接调用它所引用的函数。

phone.Ding += new DingEventHandler(people.Action);

这一行代码看似是将函数和事件建立联系,实际上是将函数封装到委托中。
还有一点值得一提,委托的签名必须和函数的签名保持一致。

3.2 事件的简化声明

namespace ConsoleApp1
{
    public delegate void DingEventHandler(Phone phone, DingEventArgs e);

    public class DingEventArgs:EventArgs
    {
        public string Message { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Phone phone = new Phone();
            People people = new People();
            phone.Ding += people.Action;
            DingEventArgs e = new DingEventArgs();
            e.Message = "Hello,World!";
            phone.MessageReceived(e);
            Console.ReadLine();
        }
    }

    public class Phone
    {
        public string ID { get; }
        public Phone()
        {
            ID = "123456";
        }
        public event DingEventHandler Ding;

        public void MessageReceived(DingEventArgs e)
        {
            Console.WriteLine("Ding,Ding!");
            Console.WriteLine("Here comes the message.");
            OnDing(e);
        }

        protected virtual void OnDing(DingEventArgs e)
        {
            Ding?.Invoke(this, e);
        }
    }

    public class People
    {
        public void Action(Phone phone, DingEventArgs e)
        {
            string id = phone.ID;
            string message = e.Message;
            Console.WriteLine("I receive message: [{0}], from {1}",message,id);
        }
    }
}

事件的简化声明省略了委托字段的显式定义,以及addremove访问器。实际上,尽管我们没有显式地将这些代码写出来,但是编译器在背后仍然会生成这些代码。
我们使用C#的反编译器看看具体情况。
将程序丢进去。

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

可见,反编译的结果中包含了私有的委托字段,以及add和remove访问器。
所以,事件的简化声明是C#为我们提供的语法糖,它让我们能够以更简单的方式、更少的代码来声明事件。但是如果我们不了解事件的完整声明的话,我们很容易误以为事件就是一个委托类型的字段,实际上并不是。

4、委托和事件的区别

委托和事件的关系,类似于字段和属性的关系。C# 类(二)——成员:字段、属性、方法、事件。

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu = new Student();
            stu.Age = 15;
            Console.WriteLine(stu.Age);
            Console.ReadLine();
        }
    }

    class Student
    {
        private int age;
        public int Age { get; set; }
    }
}

我们知道,出于数据安全的考虑,字段的访问级别我们一般设置为private,也就是只可以在类内访问,而不能在类外访问,在类外通过访问属性来间接访问字段。属性将字段进行封装,保护了数据安全。实际上,如果我们把字段的访问级别改为public,然后删掉属性,并不改变代码的运行结果,但是会给代码带来潜在的风险。

class Student
{
	public int age;
}

同样地,事件也是对委托的一种封装,事件的引入不允许在类外直接访问委托类型的字段(private),而只能通过事件(public)来间接访问委托。

在这里插入图片描述

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Bell bell = new Bell();
            Teather teather = new Teather();
            Student student = new Student();
            bell.Ring += teather.Action;
            bell.Ring += student.Action;
            bell.BellRing();
            Console.ReadLine();
        }
    }

    class Bell
    {
        public EventHandler Ring;

        public void BellRing()
        {
            Console.WriteLine("Ding!Dang!");
            OnRing(EventArgs.Empty);
        }

        protected virtual void OnRing(EventArgs e)
        {
            Ring?.Invoke(this, e);
        }
    }

    class Teather
    {
        public void Action(object sender, EventArgs e)
        {
            try
            {
                throw new Exception("Teacher throw an exception.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex.Message);
            }
        }
    }

    class Student
    {
        public void Action(object sender, EventArgs e)
        {
            try
            {
                Console.WriteLine("I settled into my seat.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: {0}", ex.Message);
            }
        }
    }
}

将代码中的event关键字删掉,代码功能由原来的委托类型事件的声明,变成了委托类型字段的声明。再次运行代码,发现运行结果并不会改变。这样的改动将委托直接暴露在了类外,给程序带来了潜在的风险。

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

比如,我们可以在类外直接调用委托的Invoke方法,这样的结果是,铃声还没有响,老师就走进教室了,学生也需要在位置上坐好,这就不符合代码逻辑了。

在这里插入图片描述

而如果我们使用的是事件的话,在类外根本就不允许有这种操作,这个时候我们想要触发事件,只能通过调用bell.BellRing();方法,而如果我们想直接通过事件来调用Invoke方法,编译器会报错事件"Bell.Ring"只能出现在+=或-=的左边,编译器压根就不允许我们有这种操作。

5、事件的作用

事件的封装性使得事件的触发逻辑和订阅者列表被封装在类内部,外部代码不能直接调用事件,也不能直接访问事件的订阅者列表,从而提高了代码的安全性和稳定性。

  • 松耦合:程序设计的一大原则是“高内聚,低耦合”,事件让发布者和订阅者之间以一种松耦合的方式联系起来,发布者不需要知道订阅者是谁,订阅者也不需要知道发布者是谁,只要订阅者的事件处理函数签名满足发布者中委托类型字段的签名,就可以形成订阅关系。
  • 灵活性:以动态调用和可扩展的方式对一件事情做出响应。动态调用指的是,当感兴趣的事情发生时,事件被触发,发布者通知订阅者采取行动;而如果感兴趣的事情没有发生,那么发布者和订阅者都保持沉默状态。可扩展指的是,我们如果想要增加订阅者列表,无需对发布者代码进行任何修改,只需要确保事件处理函数签名正确即可。
  • 发布者-订阅者模式。

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

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

相关文章

php反序列化原生态 ctfshow练习 字符串逃逸

web262 拿着题审计一下 <?php error_reporting(0); class message{public $from;public $msg;public $to;public $tokenuser;public function __construct($f,$m,$t){$this->from $f;$this->msg $m;$this->to $t;} }$f $_GET[f]; $m $_GET[m]; $t $_GET[t…

【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 一、求平方根的迭代公式 1. 原理 2. 代码实现示例 二、绝对值函数fabs() 1. 函数介绍 2. 代码示例 三、循环语句 1. for循环 2. while循环 3. do - while循环 编程要求 测试说明 通关代码 测试结果 任务描述 本关任务&…

使用 Three.js 创建动态粒子效果

今天&#xff0c;带大家使用粒子实现一个粒子飞毯的效果&#xff0c;我们先来看一下效果。 实现 初始化场景 首先创建一个场景&#xff0c;所有 3D 对象都会被添加到这个场景中。 const scene new THREE.Scene();相机和渲染器 配置相机和渲染器来捕捉和显示场景。 相机…

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World 2025/1/3 14:06 百度&#xff1a;android studio helloworld android studio hello world kotlin helloword kotlin 串口 no run configurations added android studio no run configurations added 1、…

c#使用SevenZipSharp实现压缩文件和目录

封装了一个类&#xff0c;方便使用SevenZipSharp&#xff0c;支持加入进度显示事件。 双重加密压缩工具范例&#xff1a; using SevenZip; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.…

Ubuntu 20.04安装gcc

一、安装GCC 1.更新包列表 user596785154:~$ sudo apt update2.安装gcc user596785154:~$ sudo apt install gcc3.验证安装 user596785154:~$ gcc --version二 编译C文件 1.新建workspace文件夹 user596785154:~$ mkdir workspace2.进入workspace文件夹 user596785154:~…

网络协议安全的攻击手法

1.使用SYN Flood泛洪攻击&#xff1a; SYN Flood(半开放攻击)是最经典的ddos攻击之一&#xff0c;他利用了TCP协议的三次握手机制&#xff0c;攻击者通常利用工具或控制僵尸主机向服务器发送海量的变源端口的TCP SYN报文&#xff0c;服务器响应了这些报文后就会生成大量的半连…

晨辉面试抽签和评分管理系统之一:考生信息管理和编排

晨辉面试抽签和评分管理系统&#xff08;下载地址:www.chenhuisoft.cn&#xff09;是公务员招录面试、教师资格考试面试、企业招录面试等各类面试通用的考生编排、考生入场抽签、候考室倒计时管理、面试考官抽签、面试评分记录和成绩核算的面试全流程信息化管理软件。提供了考生…

鸿蒙的APP真机调试以及发布

目录&#xff1a; 1、创建好鸿蒙项目2、创建AGC项目3、实现自动签名3.1、手动方式创建签名文件和密码 4、运行项目5、无线真机调试 1、创建好鸿蒙项目 2、创建AGC项目 &#xff08;1&#xff09;在File->Project Structure->Project->Signing Configs中进行登录。(未…

H7-TOOL固件2.27发布,新增加40多款芯片脱机烧录,含多款车轨芯片,发布LUA API手册,CAN助手增加负载率,错误状态信息检测

H7-TOOL详细介绍&#xff08;含操作手册&#xff09;&#xff1a;H7-TOOL开发工具&#xff0c;1拖4/16脱机烧录&#xff0c;高速DAPLINK&#xff0c;RTOS Trace&#xff0c;CAN/串口助手, 示波器, RTT等&#xff0c;支持WiFi&#xff0c;以太网&#xff0c;高速USB和手持 - H7-…

SpringMVC(一)配置

目录 引入 第一章&#xff1a;Java web的发展历史 一、Model I和Model II 1.Model I开发模式 2.Model II开发模式 二. MVC模式 第二章&#xff1a;SpringMVC的入门案例 搭建SpringMVC的入门程序 1.创建新项目 2.等待加载导入坐标 3.处理xml文件和其他 导入tomcat 运…

Linux驱动开发 gpio_get_value读取输出io的电平返回值一直为0的问题

当时gpio子系统进行读取时返回必定是0 因此&#xff0c;首先必须使用platform驱动来管理gpio和pinctrl子系统&#xff0c;然后如果按照正点原子所教的设备树引脚设置为0x10B0则会导致读取到的电平值为0。 解决方法&#xff1a; 将设备树中的引脚设置为 pinctrl_gpioled: gpio…

uniapp-vue3 实现, 一款带有丝滑动画效果的单选框组件,支持微信小程序、H5等多端

采用 uniapp-vue3 实现, 是一款带有丝滑动画效果的单选框组件&#xff0c;提供点状、条状的动画过渡效果&#xff0c;支持多项自定义配置&#xff0c;适配 web、H5、微信小程序&#xff08;其他平台小程序未测试过&#xff0c;可自行尝试&#xff09; 可到插件市场下载尝试&…

Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s

前言&#xff1a;纯个人记录使用。 搭建 Zero to JupyterHub with Kubernetes 上篇 - Kubernetes 离线二进制部署。搭建 Zero to JupyterHub with Kubernetes 中篇 - Kubernetes 常规使用记录。搭建 Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s。 官方文档…

倾斜摄影相机在不动产确权登记和权籍调查中的应用

一、项目背景 1.1 项目背景 为贯彻落实中央、国务院关于实施乡村振兴战略、关于“扎实推进房地一体的农村集体建设用地和宅基地使用权确权登记颁证&#xff0c;完善农民闲置宅基地和闲置农房政策&#xff0c;探索宅基地所有权、资格权、使用权‘三权分置’”的要求&#xff0…

计算机网络 (22)网际协议IP

一、IP协议的基本定义 IP协议是Internet Protocol的缩写&#xff0c;即因特网协议。它是TCP/IP协议簇中最核心的协议&#xff0c;负责在网络中传送数据包&#xff0c;并提供寻址和路由功能。IP协议为每个连接在因特网上的主机&#xff08;或路由器&#xff09;分配一个唯一的IP…

网络安全测评技术与标准

18.1 概况 1&#xff09;概念 &#xff1a;指参照一定的标准规范要求&#xff0c;通过一系列的技术和管理方法&#xff0c;获取评估对象的网络安全状况信息&#xff0c;对其给出相应的网络安全情况综合判定。 网络安全测评对象通常包括信息系统的组成要素或信息系统自身。 2…

5个不同类型的mysql数据库安装

各种社区版本下载官方地址&#xff1a;MySQL :: MySQL Community Downloads 一、在线YUM仓库&#xff08;Linux&#xff09; 选择 MySQL Yum Repository 选择对应版本下载仓库安装包&#xff08;No thanks, just start my download.&#xff09; 下载方法1&#xff1a;下载到本…

Lua开发环境如何安装?保姆级教程

大家好&#xff0c;我是袁庭新。Lua开发环境如何安装搭建&#xff1f;这套篇文章帮你搞定&#xff5e; CentOS 7系统默认已经安装了Lua语言环境&#xff0c;因此可直接运行Lua代码。可以使用以下命令查看当前系统中默认自带的Lua版本。 # 查看系统默认自带的Lua版本 [rootloc…

Linux 系统搭建网络传输环境汇总

Ubuntu 系统搭建 TFTP 服务器 1. 创建 /home/username/workspace/tftp 目录并赋予最大权限&#xff0c;username 是自己用户名 sudo mkdir -p /home/username/workspace/tftp sudo chmod 777 /home/username/workspace/tftp 2. 安装 tftp-hpa&#xff08; 客户端软件包&#x…