Go学习第十二章——Go反射与TCP编程

news2024/11/18 21:39:35

Go反射与TCP编程

      • 1 反射
        • 1.1 基本介绍
        • 1.2 快速入门
        • 1.3 注意事项和细节说明
        • 1.4 最佳实践
      • 2 Tcp Socket编程
        • 2.1 基本介绍
        • 2.2 入门案例
        • 2.3 服务器监听
        • 2.4 服务器接受客户端消息

1 反射

1.1 基本介绍

**反射:**在编译时静态类型语言中实现动态特性的一种机制。

Go语言通过反射提供了一种在运行时检查类型和访问类型成员的能力,它可以在不知道具体类型的情况下,动态地获取和修改变量的值、调用方法等。

  1. 反射可以咋子运行时动态获取变量的各种信息,比如:变量的类型,类别
  2. 如果是结构体遍历,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  3. 通过反射,可以修改变量的值,可以调用关联的方法。
  4. 使用反射,需要 import (“reflect”)

image-20231027142557365

使用场景:

  1. JSON序列化和反序列化:根据结构体的字段和标签信息,通过反射可以动态地进行JSON的序列化和反序列化。
  2. ORM框架:通过反射可以解析结构体字段和数据库表的映射关系,实现自动化的ORM操作。
  3. 动态调用函数和方法:通过反射可以根据函数或方法的名称动态地进行调用。
  4. 接口的实现判断:通过反射可以判断一个类型是否实现了某个接口,从而在运行时动态地处理接口类型。
  5. 插件系统:通过反射可以在运行时动态加载和执行插件功能。
1.2 快速入门

反射中重要的函数

  1. reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
  2. reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型。reflect.Value 是一个结构体类型。

注意:变量、interface{} 和 reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。

image-20231027151101752

  1. 变量 转 interface{}
var name string = "John"
var i interface{} = name
fmt.Printf("%T %v\n", i, i) // 输出: string John
  1. interface{} 转 变量
var j interface{} = "Tom"
var name2 string
name2, ok := j.(string)
if ok {
	fmt.Printf("%T %v\n", name2, name2) // 输出: string Tom
} else {
	fmt.Println("conversion failed")
}
  1. reflect.Value 转变量

函数:func ValueOf(i interface{}) Value

作用:ValueOf返回一个初始化为 i 接口保管的具体值的Value,ValueOf(nil)返回Value零值。

var age int = 42
value := reflect.ValueOf(age)
valueInt := value.Interface()
age2, ok := valueInt.(int)
if ok {
	fmt.Printf("%T %v\n", age2, age2) // 输出: int 42
} else {
	fmt.Println("conversion failed")
}

详细讲解一下:

  • 首先定义了一个 age 变量,类型为 int,并赋值为 42。接下来使用 reflect.ValueOf() 函数将 age 转换为 reflect.Value 类型,并将结果赋值给变量 value

  • reflect.Value 是反射库 reflect 提供的一个类型,用于存储和操作变量的信息。它包含了变量的值以及其相关的类型信息。

  • 为了能够方便地对变量进行操作,我们可以通过 reflect.Value 调用其 Interface() 方法,将其转换为 interface{} 类型的变量。这样就可以得到原本变量的值,只不过类型变为了 interface{}。将结果赋值给变量 valueInt

  1. 变量 转 reflect.Value
var score float32 = 98.5
value2 := reflect.ValueOf(score)
fmt.Printf("%T %v\n", value2, value2) // 输出: reflect.Value
  1. reflect.Value 转 interface{}
var flag bool = true
value3 := reflect.ValueOf(flag)
valueInterface := value3.Interface()
flag2, ok := valueInterface.(bool)
if ok {
	fmt.Printf("%T %v\n", flag2, flag2) // 输出: bool true
} else {
	fmt.Println("conversion failed")
}
  1. 结构体 转 Interface{}, reflect.Value
type Student struct {
    Name  string
    Age   int
    score float64
}

func main() {
    stu := Student{"Tom", 22, 88.4}

    // 结构体 转 interface{}
    var i interface{} = stu
    fmt.Printf("i的类型:%T ,值:%v\n", i, i)

    // 结构体 转 reflect.Value
    value := reflect.ValueOf(stu)
    fmt.Printf("value的类型:%T ,值:%v\n", value, value) 
    
    // reflect.Value 转 结构体
    valueInterface := value.Interface()
    // 断言,看看是否转换成功
    flag1, ok := valueInterface.(Student)
    if ok {
       fmt.Printf("flag1的类型:%T ,值:%v\n", flag1, flag1) 
    } else {
       fmt.Println("conversion failed")
    }
}

输出结果:

i的类型:main.Student ,值:{Tom 22 88.4}
value的类型:reflect.Value ,值:{Tom 22 88.4}
flag1的类型:main.Student ,值:{Tom 22 88.4}
1.3 注意事项和细节说明

补充:

常量介绍

  1. 常量使用const 修饰
  2. 常量在定义的时候,必须初始化
  3. 常量不能修改
  4. 常量只能修饰bool、数值类型(int, float系列)、string类型
  5. 语法:const identifier [type] = value
  6. 举例说明:
  • const name = “tom” // ok
  • const tax float64 = 0.8 // ok
  • const a int // error
  • const b = 9 / 3 // ok
  • const c = getVal() // err
  1. 注意事项:

    1. Golang 中 没有常量名必须字母大写的规范,比如:TAX_RATE等

    2. 常量仍然通过首字母的大小写来控制常量的访问范围。

反射的注意事项:

  1. reflect.Value.Kind,获取变量的类别,返回的是一个常量。

    方法:func (k Kind) String() string

    func main() {
    	stu := Student{"Tom", 18, 88.1}
    	stuType := reflect.TypeOf(stu)
    	kind := stuType.Kind()
    	fmt.Println("kind=", kind) // 输出:kind= struct
    }
    
  2. Type是类型,Kind是类别,Type 和 Kind 可能是相同的,也可能是不同的。

比如:var num int = 10,num的Type是int,Kind也是int

比如:var stu Student stu的Type是 包名.Student,Kind是struct

  1. 通过反射可以让变量在interface[]和Reflect.Value之间相互转换

  2. 使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如 x 是 int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报panic。

  3. 通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法。

方法:func (v Value) Elem() Value

作用:Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。

func reflect01(i interface{}) {
	// 2. 获取到reflect.Value
	rVal := reflect.ValueOf(i)
	// 看看 rVal的Kind是啥
	fmt.Printf("rVal kind=%v\n", rVal.Kind())
	// 3.通过这里直接设置,下面num的值
	// rVal.SetInt(20) // 报错,这里的rVal是个指针
	rVal.Elem().SetInt(20) // 成功
}
func main() {
	var num int = 10
	reflect01(&num)
	fmt.Println("num=", num)
}
1.4 最佳实践

案例:使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值。

//定义了一个Monster结构体
type Monster struct {
    Name  string `json:"name"`
    Age   int `json:"monster_age"`
    Score float32 `json:"成绩"`
    Sex   string
    
}

//方法,返回两个数的和
func (s Monster) GetSum(n1, n2 int) int {
    return n1 + n2
}
//方法, 接收四个值,给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
    s.Name = name
    s.Age = age
    s.Score = score
    s.Sex = sex
}

//方法,显示s的值
func (s Monster) Print() {
    fmt.Println("---start~----")
    fmt.Println(s)
    fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
    //获取reflect.Type 类型
    typ := reflect.TypeOf(a)
    //获取reflect.Value 类型
    val := reflect.ValueOf(a)
    //获取到a对应的类别
    kd := val.Kind()
    //如果传入的不是struct,就退出
    if kd !=  reflect.Struct {
       fmt.Println("expect struct")
       return
    }

    //获取到该结构体有几个字段
    num := val.NumField()

    fmt.Printf("struct has %d fields\n", num) //4
    //变量结构体的所有字段
    for i := 0; i < num; i++ {
       fmt.Printf("Field %d: 值为=%v\n", i, val.Field(i))
       //获取到struct标签, 注意需要通过reflect.Type来获取tag标签的值
       tagVal := typ.Field(i).Tag.Get("json")
       //如果该字段于tag标签就显示,否则就不显示
       if tagVal != "" {
          fmt.Printf("Field %d: tag为=%v\n", i, tagVal)
       }
    }
    
    //获取到该结构体有多少个方法
    numOfMethod := val.NumMethod()
    fmt.Printf("struct has %d methods\n", numOfMethod)
    
    //var params []reflect.Value
    //方法的排序默认是按照 函数名的排序(ASCII码)
    val.Method(1).Call(nil) //获取到第二个方法。调用它

    
    //调用结构体的第1个方法Method(0)
    var params []reflect.Value  //声明了 []reflect.Value
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    res := val.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Value
    fmt.Println("res=", res[0].Int()) //返回结果, 返回的结果是 []reflect.Value*/

}
func main() {
    //创建了一个Monster实例
    var a Monster = Monster{
       Name:  "黄鼠狼精",
       Age:   400,
       Score: 30.8,
    }
    //将Monster实例传递给TestStruct函数
    TestStruct(a)  
}

输出结果:

struct has 4 fields
Field 0: 值为=黄鼠狼精
Field 0: tag为=name
Field 1: 值为=400
Field 1: tag为=monster_age
Field 2: 值为=30.8
Field 2: tag为=成绩
Field 3: 值为=
struct has 3 methods
---start~----
{黄鼠狼精 400 30.8 }
---end~----
res= 50

过程并不复杂,主要的是TetsSturct函数里面的内容,就是徐徐渐进,直到获得对应的值。

2 Tcp Socket编程

这里需要前置知识,计算机网络部分的知识,如果没有,建议去好好看一下,再往下学~~

2.1 基本介绍

image-20231027165335796

服务端处理流程:

  1. 监听端口 8888
  2. 接收客户端的tcp链接,简历客户端和服务端的链接
  3. 创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)

客户端处理流程:

  1. 建立与服务端的链接
  2. 发送请求数据[终端],接收服务器端返回的结果数据
  3. 关闭链接

简单的程序示意图:

image-20231027165924840

2.2 入门案例

服务器端功能:

  1. 编写一个服务器程序,在8888端口监听
  2. 可以和多个客户端创建链接
  3. 链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上。
  4. 先使用telnet来测试,然后编写客户端程序来测试。

**官方文档:**https://studygolang.com/pkgdoc

image-20231027170458418

入门案例代码里的一些包,结构体,函数等提前预告:

Listener是一个用于面向流的网络协议的公用的网络监听器接口。多个线程可能会同时调用一个Listener的方法。

type Listener interface {
    // Addr返回该接口的网络地址
    Addr() Addr
    // Accept等待并返回下一个连接到该接口的连接
    Accept() (c Conn, err error)
    // Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误。
    Close() error
}

**函数:**func Listen(net, laddr string) (Listener, error)

**作用:**返回在一个本地网络地址laddr上监听的Listener。网络类型参数net必须是面向流的网络:

“tcp”、“tcp4”、“tcp6”、“unix"或"unixpacket”。参见Dial函数获取laddr的语法。

Conn接口代表通用的面向流的网络连接。多个线程可能会同时调用同一个Conn的方法。

type Conn interface {
    // Read从连接中读取数据
    // Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
    Read(b []byte) (n int, err error)
    // Write从连接中写入数据
    // Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
    Write(b []byte) (n int, err error)
    // Close方法关闭该连接
    // 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
    Close() error
    // 返回本地网络地址
    LocalAddr() Addr
    // 返回远端网络地址
    RemoteAddr() Addr
    // 设定该连接的读写deadline,等价于同时调用SetReadDeadline和SetWriteDeadline
    // deadline是一个绝对时间,超过该时间后I/O操作就会直接因超时失败返回而不会阻塞
    // deadline对之后的所有I/O操作都起效,而不仅仅是下一次的读或写操作
    // 参数t为零值表示不设置期限
    SetDeadline(t time.Time) error
    // 设定该连接的读操作deadline,参数t为零值表示不设置期限
    SetReadDeadline(t time.Time) error
    // 设定该连接的写操作deadline,参数t为零值表示不设置期限
    // 即使写入超时,返回值n也可能>0,说明成功写入了部分数据
    SetWriteDeadline(t time.Time) error
}

**函数:**func Dial(network, address string) (Conn, error)

**作用:**在网络network上连接地址address,并返回一个Conn接口。可用的网络类型有:

“tcp”、“tcp4”、“tcp6”、“udp”、“udp4”、“udp6”、“ip”、“ip4”、“ip6”、“unix”、“unixgram”、“unixpacket”

对TCP和UDP网络,地址格式是host:port或[host]:port,参见函数JoinHostPort和SplitHostPort。

2.3 服务器监听

我们想写一个服务器来监听

创建一个server.go文件,作为服务器,简单写一下服务器的监听功能

func main() {

    fmt.Println("服务器开始监听。。。")
    // net.Listen("tcp", "0.0.0.0:8888")
    // 1. tcp 表示使用网络协议是tcp
    // 2. 0.0.0.0:8888 表示在本地监听 8888端口
    listen, err := net.Listen("tcp", "0.0.0.0:8888")
    if err != nil {
       fmt.Println("listen err=", err)
       return
    }
    defer listen.Close() // 延时关闭listen

    // 循环等带客户端来连接我
    for {
       // 等待客户端连接
       fmt.Println("等待客户端来连接。。。")
       // Accept等待并返回下一个链接到该接口的连接
       conn, err := listen.Accept()
       if err != nil {
          // 连接失败
          fmt.Println("Accept() err=", err)
       } else {
          // 连接成功
          fmt.Printf("Accept() suc con=%v\n", conn)
       }
       // 这里准备起一个协程,为客户端服务
    }
}
  1. 使用net.Listen()函数创建一个监听器,监听0.0.0.0:8888地址。错误处理会打印出错误信息并返回。

  2. 使用defer关键字延迟关闭监听器。**注意:**一定要关闭,不然会一直占用资源。

  3. 进入无限循环,等待客户端的连接。在每次循环中,使用listen.Accept()函数接受客户端的连接请求。如果连接成功,则会打印连接信息。如果出现错误,则会打印错误信息。

测试一下:

使用telnet,可以测试这个服务器能不能监听,telnet需要下载,网上有教程,去搜搜

我们运行这个server.go,下面会显示:

服务器开始监听。。。
等待客户端来连接。。。

然后,cmd,打开命令行,输入:telnet 127.0.0.1 8888,就会进入telnet,然后我们看一下server.go

image-20231027173038415

从命令行就可以看出,连接成功过~~

2.4 服务器接受客户端消息

上一节,我们测试是否能够连接,下面我们写个客户端发送消息给服务器

创建client文件夹作为客户端,然后创建client.go文件

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8888")
    if err != nil {
       fmt.Println("client dial err=", err)
       return 
    }
    //功能一:客户端可以发送单行数据,然后就退出
    reader := bufio.NewReader(os.Stdin) //os.Stdin 代表标准输入[终端]

    for {

       //从终端读取一行用户输入,并准备发送给服务器
       line, err := reader.ReadString('\n')
       if err != nil {
          fmt.Println("readString err=", err)
       }
       //如果用户输入的是 exit就退出
       line = strings.Trim(line, " \r\n")
       if line == "exit" {
          fmt.Println("客户端退出..")
          break
       }

       //再将line 发送给 服务器
       _, err = conn.Write([]byte(line + "\n"))
       if err != nil {
          fmt.Println("conn.Write err=", err)    
       }
    }
}

然后客户端写完之后,我们去server里写协程:

func process(conn net.Conn) {

    //这里我们循环的接收客户端发送的数据
    defer conn.Close() //关闭conn

    for {
       //创建一个新的切片
       buf := make([]byte, 1024)
       //conn.Read(buf)
       //1. 等待客户端通过conn发送信息
       //2. 如果客户端没有wrtie[发送],那么协程就阻塞在这里
       //fmt.Printf("服务器在等待客户端%s 发送信息\n", conn.RemoteAddr().String())
       n, err := conn.Read(buf) //从conn读取
       if err != nil {

          fmt.Printf("客户端退出 err=%v", err)
          return //!!!
       }
       //3. 显示客户端发送的内容到服务器的终端
       fmt.Print(string(buf[:n]))
    }
}

这样就写好啦!!~~

下一步,我们先运行server,再运行client,测试一下~

image-20231027174118616

然后,我们下面client输入hello world

image-20231027174232077

并且还能退出,在终端输入exit退出

image-20231027174340002

完美~~~~

这样就Go学习基础快速入门就基本完成啦!!!!后面将会学习与MySQL的连接和Redis的连接,冲冲冲!!!

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

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

相关文章

.NET 控制台NLog 使用

前言 我们需要一个日志Log去记录日常发生的信息&#xff0c;这样方便以后出现问题的时候回去找报错的日志 NLog NLog 是Log的.net 移植版本&#xff0c;.net 将很多别的已经成熟的第三方库进行了移植。 NLog使用 NLog 官网文档 NLog .net core 官方文档 NLog详细教程-2022/…

Python武器库开发-高级特性篇(七)

高级特性篇(七) 装饰器 装饰器是给现有的模块增添新的小功能&#xff0c;可以对原函数进行功能扩展&#xff0c;而且还不需要修改原函数的内容&#xff0c;也不需要修改原函数的调用。这也称为元编程&#xff0c;因为程序的一部分试图在编译时修改程序的另一部分。Python中的…

通达信涨停回马枪选股公式,涨停回调不破起涨点

在编写涨停回马枪选股公式之前&#xff0c;我们需要先深入了解如何编写涨停公式。在前面的文章《创业板、科创板也适用的通达信涨停指标公式怎么写&#xff1f;》中介绍了涨停指标公式的编写方法&#xff0c;然而这种方法偶尔还是会有漏网之鱼。因此借此机会&#xff0c;我们将…

【C++】多态 ② ( 面向对象中 “ 多态 “ 的真正需求 | 面向对象 “ 多态 “ 实现 - virtual 修饰函数 | 代码示例 )

文章目录 一、多态实现1、面向对象中 " 多态 " 的真正需求2、面向对象 " 多态 " 实现 - virtual 修饰函数 二、代码示例 - 多态实现1、代码示例2、执行结果 一、多态实现 1、面向对象中 " 多态 " 的真正需求 在上一篇博客 【C】多态 ① ( 类型兼…

2017-2022年中国地方ZF数据开放指数数据/历年开放数林指数数据集(省域指数、城市指数)

2017-2022年中国地方ZF数据开放指数数据/历年开放数林指数数据集&#xff08;省域指数、城市指数&#xff09; 1、时间&#xff1a;2017-2021年 2、指标&#xff1a;地方、准备度指数、准备度排名、平台层指数、平台层排名、数据层指数、数据层排名、利用层指数、利用层排名…

【Linux】部署单体项目以及前后端分离项目(项目部署)

一、简介 以下就是Linux部署单机项目和前后端分离项目的优缺点&#xff0c;希望对你有所帮助。 1、Linux部署单机项目&#xff1a; 优点&#xff1a; 简化了系统管理&#xff1a;由于所有服务都在同一台机器上运行&#xff0c;因此可以简化系统管理和维护。提高了性能&#x…

三大电商平台(淘宝/京东/阿里巴巴)封装商品详情API接口附代码实例|参数解析

接口的特点 Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔) Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化 Java接口中只能包含publi…

[PyTorch][chapter 58][强化学习-1]

前言&#xff1a; RL(Reinfocement Learning) 强化学习 是机器学习&#xff0c;深度学习一个重点。 后面20章将重点结合一些例子回顾一下经典的强化学习算法。 这里重点介绍一下机器学习中的强化学习算法&#xff0c;以及Gym 工具 目录&#xff1a; 简介 强化学习基本要素 …

释放搜索潜力:基于ES(ElasticSearch)打造高效的语义搜索系统,让信息尽在掌握[2.项目讲解篇],支持Linux/Windows部署安装

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

智慧矿山系统中的猴车安全监测与识别

智慧矿山是近年来兴起的一种采用人工智能&#xff08;AI&#xff09;技术的矿山管理方式&#xff0c;它通过利用智能传感设备和先进算法来实现对矿山环境和设备进行监测和管理&#xff0c;从而提高矿山的安全性和效率。在智慧矿山的AI算法系列中&#xff0c;猴车不安全行为识别…

js中HTMLCollection如何循环

//不带索引 let divCon document.getElementsByClassName("el-form-item__error"); if (divCon.length > 0) {for (var item of divCon) {console.log("打印&#xff1a;", item.innerText);} }//带有索引 let divCon document.getElementsByClassNam…

毅速丨增减材协同制造已逐渐成为趋势

近年来&#xff0c;增材制造3D打印技术的发展非常迅速&#xff0c;被广泛应用于航空航天、汽车、电子、医疗等许多行业。增材制造技术通过逐层增加材料的方式制造出各种复杂形状的零件&#xff0c;具有很高的制造效率和灵活性。 然而&#xff0c;在精密加工领域&#xff0c;增材…

如何学好C++?学习C和C++的技巧是什么?

如何学好C?学习C和C的技巧是什么&#xff1f; 你这三个问题&#xff0c;前两个都是意思是差不多的&#xff0c;那么怎么怎么学习C/C我来问答一下&#xff1a;最近很多小伙伴找我&#xff0c;说想要一些C资料&#xff0c;然后我根据自己从业十年经验&#xff0c;熬夜搞了几个通…

二叉树问题——对称二叉树

摘要 101. 对称二叉树 一、对称二叉树解析 1.1 递归思路分析 首先想清楚&#xff0c;判断对称二叉树要比较的是哪两个节点&#xff0c;要比较的可不是左右节点&#xff01;对于二叉树是否对称&#xff0c;要比较的是根节点的左子树与右子树是不是相互翻转的&#xff0c;理解…

Map集合的遍历:键值对

package day01;import java.util.*;public class Mapday1 {public static void main(String[] args) {/* HashMap 无序 不重复&#xff0c;会覆盖前面 无索引*/System.out.println("--------------------");Map<String, Integer> map new HashMap<>();m…

如何设置模型的粗糙质感?

1、粗糙贴图的原理 粗糙贴图&#xff08;Roughness Map&#xff09;是一种用于模拟物体表面粗糙程度的贴图技术。它通过控制光线在物体表面的散射程度来实现不同粗糙度的效果。粗糙贴图通常使用灰度图像来表示不同部分的粗糙度&#xff0c;白色表示较光滑的表面&#xff0c;黑…

敏捷战略下的目标管理

1. 生而敏捷的 OKR 敏捷战略规划的周期相对较长&#xff0c;一般是以年为单位在做规划&#xff0c;通常是 3~5年。在战略规划之后&#xff0c;需要有更短周期的目标管理去做承接。现今&#xff0c; OKR 成为承接敏捷战略最好的目标管理工具。 将OKR 和战略、愿景、使命之间的关…

【网络工程师8个疑难杂症解决方案】

典型问题① 用户抱怨:整个网络变慢 网络症状:影响所有连接到网段上的工作站 查找问题:在正常的网络使用时段&#xff0c;将网络测试仪连接到集线器&#xff0c;在光纤连接的两端产生流量&#xff0c;测试每边健康状况。在某一时刻断开或接上光纤&#xff0c;每当光纤连入网段…

基于GPIO子系统编写LED驱动

编写应用程序进行测试 设置定时器&#xff0c;每5秒打印一次hello world 驱动程序 #include <linux/init.h> #include <linux/module.h> #include<linux/of.h> #include<linux/of_gpio.h> #include<linux/fs.h> #include<linux/io.h> #i…

笔记-《RabbitMQ实战指南》

目录 1.发到Mq对应的3个参数&#xff1a;Exchange(交换器)&#xff0c;RountingKey(路由键)&#xff0c;param2.队列3.Exchange(交换器)4.Binding(绑定)5.交换器类型6.Connection(连接)&#xff0c;Channel(信道)7. AMQP协议8. 判断Channel或者Connection关闭状态9. 交换器和队…