Go学习第十二章——Go反射与TCP编程
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语言通过反射提供了一种在运行时检查类型和访问类型成员的能力,它可以在不知道具体类型的情况下,动态地获取和修改变量的值、调用方法等。
- 反射可以咋子运行时动态获取变量的各种信息,比如:变量的类型,类别
- 如果是结构体遍历,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法。
- 使用反射,需要 import (“reflect”)
使用场景:
- JSON序列化和反序列化:根据结构体的字段和标签信息,通过反射可以动态地进行JSON的序列化和反序列化。
- ORM框架:通过反射可以解析结构体字段和数据库表的映射关系,实现自动化的ORM操作。
- 动态调用函数和方法:通过反射可以根据函数或方法的名称动态地进行调用。
- 接口的实现判断:通过反射可以判断一个类型是否实现了某个接口,从而在运行时动态地处理接口类型。
- 插件系统:通过反射可以在运行时动态加载和执行插件功能。
1.2 快速入门
反射中重要的函数
- reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
- reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型。reflect.Value 是一个结构体类型。
注意:变量、interface{} 和 reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。
- 变量 转 interface{}
var name string = "John"
var i interface{} = name
fmt.Printf("%T %v\n", i, i) // 输出: string John
- 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")
}
- 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
。
- 变量 转 reflect.Value
var score float32 = 98.5
value2 := reflect.ValueOf(score)
fmt.Printf("%T %v\n", value2, value2) // 输出: reflect.Value
- 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")
}
- 结构体 转 Interface{}, reflect.Value
type Student struct {Name stringAge intscore float64
}func main() {stu := Student{"Tom", 22, 88.4}// 结构体 转 interface{}var i interface{} = stufmt.Printf("i的类型:%T ,值:%v\n", i, i)// 结构体 转 reflect.Valuevalue := 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 注意事项和细节说明
补充:
常量介绍
- 常量使用const 修饰
- 常量在定义的时候,必须初始化
- 常量不能修改
- 常量只能修饰bool、数值类型(int, float系列)、string类型
- 语法:const identifier [type] = value
- 举例说明:
- const name = “tom” // ok
- const tax float64 = 0.8 // ok
- const a int // error
- const b = 9 / 3 // ok
- const c = getVal() // err
-
注意事项:
-
Golang 中 没有常量名必须字母大写的规范,比如:TAX_RATE等
-
常量仍然通过首字母的大小写来控制常量的访问范围。
-
反射的注意事项:
-
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 }
-
Type是类型,Kind是类别,Type 和 Kind 可能是相同的,也可能是不同的。
比如:var num int = 10,num的Type是int,Kind也是int
比如:var stu Student stu的Type是 包名.Student,Kind是struct
-
通过反射可以让变量在interface[]和Reflect.Value之间相互转换
-
使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如 x 是 int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报panic。
-
通过反射来修改变量,注意当使用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.ValuerVal := 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 = 10reflect01(&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 = names.Age = ages.Score = scores.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.Valueparams = append(params, reflect.ValueOf(10))params = append(params, reflect.ValueOf(40))res := val.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Valuefmt.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 基本介绍
服务端处理流程:
- 监听端口 8888
- 接收客户端的tcp链接,简历客户端和服务端的链接
- 创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)
客户端处理流程:
- 建立与服务端的链接
- 发送请求数据[终端],接收服务器端返回的结果数据
- 关闭链接
简单的程序示意图:
2.2 入门案例
服务器端功能:
- 编写一个服务器程序,在8888端口监听
- 可以和多个客户端创建链接
- 链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上。
- 先使用telnet来测试,然后编写客户端程序来测试。
**官方文档:**https://studygolang.com/pkgdoc
入门案例代码里的一些包,结构体,函数等提前预告:
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)}// 这里准备起一个协程,为客户端服务}
}
-
使用
net.Listen()
函数创建一个监听器,监听0.0.0.0:8888地址。错误处理会打印出错误信息并返回。 -
使用
defer
关键字延迟关闭监听器。**注意:**一定要关闭,不然会一直占用资源。 -
进入无限循环,等待客户端的连接。在每次循环中,使用
listen.Accept()
函数接受客户端的连接请求。如果连接成功,则会打印连接信息。如果出现错误,则会打印错误信息。
测试一下:
使用telnet,可以测试这个服务器能不能监听,telnet需要下载,网上有教程,去搜搜
我们运行这个server.go,下面会显示:
服务器开始监听。。。
等待客户端来连接。。。
然后,cmd,打开命令行,输入:telnet 127.0.0.1 8888
,就会进入telnet,然后我们看一下server.go
从命令行就可以看出,连接成功过~~
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() //关闭connfor {//创建一个新的切片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,测试一下~
然后,我们下面client输入hello world
并且还能退出,在终端输入exit退出
完美~~~~
这样就Go学习基础快速入门就基本完成啦!!!!后面将会学习与MySQL的连接和Redis的连接,冲冲冲!!!
相关文章:

Go学习第十二章——Go反射与TCP编程
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语言…...
uniapp编译微信小程序富文本rich-text的图片样式不生效原因
this.detail.contents this.detail.contents.replace(/\<img/gi, <img style"display:block;max-width:90%;height:auto;border:2px solid #eee;box-shadow:5px 5px 5px rgba(100,100,100,0.8);margin-bottom:10px;text-align:center;" );开始采用这个replace…...

Django实战项目-学习任务系统-任务管理
接着上期代码框架,开发第3个功能,任务管理,再增加一个学习任务表,用来记录发布的学习任务的标题和内容,预计完成天数,奖励积分和任务状态等信息。 第一步:编写第三个功能-任务管理 1࿰…...
ubuntu18.04设置开机自动启动脚本(以自动启动odoo命令行为例讲解)
简介 ubuntu作为服务器使用时,常常需要在机器重启时能自动启动我们开发的服务。 Ubuntu 16.10开始不再使用initd管理系统,改用systemd,包括用systemctl命令来替换了service和chkconfig的功能。 systemd 默认读取 /etc/systemd/system 下的配…...
golang工程——grpc-gateway 转发http header中自定义字段到grpc上下文元数据
http header 转发到 grpc上下文 grpc网关可以将请求体内容转发到grpc对应消息中。那如何获取http header头中的信息,本文将介绍如何将http header转发到grpc上下文并采用拦截器,获取http header中的内容。 有些http header中的内置字段是会转发的比如Au…...

CPU眼里的C/C++: 1.3 汇编级单步调试函数执行过程
1. 目的 2. 基于 GDB 的汇编级单步调试 原始代码 #include <stdio.h>long test() {long a 1;a 2;return a; }int main() {int ret test();printf("test return %d\n", ret);return 0; }关键 gdb 命令 si 指令执行汇编级的单步调试info registers 读取寄…...

数据结构时间复杂度(补充)和空间复杂度
Hello,今天事10月27日,距离刚开始写博客已经过去挺久了,我也不知道是什么让我坚持这么久,但是学校的课真的很多,很少有时间多出来再学习,有些科目马上要考试了,我还不知道我呢不能过哈哈哈&…...

Mac-postman存储文件目录
今天postman弹窗要求登录账号才可访问之前的API文档数据。 但是这postman的账号又是前同事的账号,我没有他的账号和密码啊。 登录了我自己的postman账号后,所有的api文档都不见了....我服了。 首先去屏幕左上角---> 前往 --->个人 然后键盘按显…...

JAVA面试题简单整理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、重载和重写的区别一、&和&&的区别一、get和post请求的区别 delete、put一、cookie和session的区别一、Autowired和Resource区别一、”和equals…...
dd命令用法学习,是一个功能强大的工具
dd 命令是一个功能强大的工具,它有许多参数可以用来控制其行为。以下是 dd 命令中常用的一些参数: - ifinputfile:指定输入文件的路径。 - ofoutputfile:指定输出文件的路径。 - bssize:设置每个块的大小。可以使用不同…...

Games104现代游戏引擎笔记 网络游戏进阶架构
Character Movement Replication 角色位移同步 玩家2的视角看玩家1的移动是起伏一截一截,并且滞后的 interpolation:内插值,在两个旧的但已知的状态计算 extrapolation:外插值,本质是预测 内插值:但网络随着…...

Apollo 快速上手指南:打造自动驾驶解决方案
快速上手 概述云端体验登录云端仿真环境 打开DreamView播放离线数据包PNC Monitor 内置的数据监视器cyber_monitor 实时通道信息视图福利活动 主页传送门:📀 传送 概述 Apollo 开放平台是一个开放的、完整的、安全的平台,将帮助汽车行业及自…...
C现代方法(第14章)笔记——预处理器
文章目录 第14章 预处理器14.1 预处理器的工作原理14.2 预处理指令14.3 宏定义14.3.1 简单的宏14.3.2 带参数的宏14.3.3 #运算符14.3.4 ##运算符14.3.5 宏的通用属性14.3.6 宏定义中的圆括号14.3.7 创建较长的宏14.3.8 预定义宏14.3.9 C99中新增的预定义宏14.3.10 空的宏参数(C…...

Kafka KRaft模式探索
1.概述 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。其核心组件包含Producer、Broker、Consumer,以及依赖的Zookeeper集群。其中Zookeeper集群是Kafka用来负责集群元数据的管理、控制器的选举等。 2.内容…...

LVS-keepalived实现高可用
概念: 本章核心: Keepalived为LVS应运而生的高可用服务。LVS的调度无法做高可用,预算keepalived这个软件,实现了调度器的高可用。 但是:Keeplived不是专门为LVS集群服务的,也可以做其他服务器的高可用 LVS…...
Linux内核驱动开发的需要掌握的知识点
Linux内核驱动开发是一项复杂而有挑战性的任务,需要掌握多方面的知识和技能。下面是一些需要掌握的关键知识点,这些知识将有助于你成功地开发Linux内核驱动程序。 1. Linux内核基础知识 首先,了解Linux内核的基础知识至关重要。这包括Linux…...

nginx 动静分离 防盗链
一、动静分离环境准备静态资源配置(10.36.192.169)安装nginx修改配置文件重启nginx 动态资源配置(192.168.20.135)yum安装php修改nginx配置文件重启nginx nginx代理机配置(192.168.20.134)修改nginx子自配置文件重启nginx 客户端访问 二、防盗链nginx防止…...
MYSQL(索引篇)
一、什么是索引 索引是一种数据结构,它用来帮助MYSQL更高效的获取数据 采用索引可以提高数据检索的效率,降低IO成本 通过索引对数据排序,降低数据排序成本,降低CPU消耗 常见的有:B树索引、B树索引、哈希索引。其中Inno…...

Java API访问HDFS
一、下载IDEA 下载地址:https://www.jetbrains.com/idea/download/?sectionwindows#sectionwindows 拉到下面使用免费的IC版本即可。 运行下载下来的exe文件,注意安装路径最好不要安装到C盘,可以改成其他盘,其他选项按需勾选即可…...

高三高考免费试卷真题押题知识点合集
发表于安徽 温馨提示:有需要的真题试卷可联系本人,百卷内上免费资源。 感觉有用的下方三连,谢谢 。 免费版卷有6-60卷每卷平均4-30页 高三免费高三地理高三英语高三化学高三物理高三语文高三历史高三政治高三数学高三生物 付费版卷有1…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...