当前位置: 首页 > news >正文

UDP程序设计

UDP协议概述

UDP,User Datagram Protocol,用户数据报协议,是一个简单的面向数据报(package-oriented)的传输层协议,规范为:RFC 768。

UDP提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。缺乏可靠性,缺乏拥塞控制(congestion control)。

基本示例

由于UDP是“无连接”的,所以服务器端不需要创建监听套接字,只需要监听地址,等待客户端与之建立连接,即可通信。

net包支持的典型UDP函数:

 // 解析UDPAddrfunc ResolveUDPAddr(network, address string) (*UDPAddr, error)// 监听UDP地址func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)// 连接UDP服务器func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)// UDP读func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error)// UDP写func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error)

编写示例,一次读写操作:

服务端流程:

  • 解析UDP地址

  • 监听UDP

  • 读内容

  • 写内容

 func UDPServerBasic() {// 1.解析地址laddr, err := net.ResolveUDPAddr("udp", ":9876")if err != nil {log.Fatalln(err)}​// 2.监听udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())defer udpConn.Close()​// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())​// 4.写data := []byte("received:" + string(buf[:rn]))wn, err := udpConn.WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())}​

客户端流程:

  • 建立连接

  • 写操作

  • 读操作

 func UDPClientBasic() {// 1.建立连接raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")if err != nil {log.Fatalln(err)}udpConn, err := net.DialUDP("udp", nil, raddr)if err != nil {log.Fatalln(err)}log.Println(udpConn)​// 2.写data := []byte("Go UDP program")wn, err := udpConn.Write(data) // WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())​// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())}

测试:

 > go test -run UDPServerBasic2023/05/25 17:26:34 UDP server is listening on [::]:98762023/05/25 17:29:22 received Go UDP program from 127.0.0.1:586572023/05/25 17:29:24 send received:Go UDP program(23) to 127.0.0.1:58657
 > go test -run UDPClientBasic2023/05/25 17:29:22 &{{0xc000108f00}}2023/05/25 17:29:22 send Go UDP program(14) to 127.0.0.1:98762023/05/25 17:29:24 received received:Go UDP program from 127.0.0.1:9876

connected和unconnected的UDPConn

UDP的连接分为:

  • 已连接,connected, 使用方法 DialUDP建立的连接,称为已连接,pre-connected

  • 未连接,unconnected,使用方法 ListenUDP 获得的连接,称为未连接,not connected

如果 *UDPConnconnected,读写方法 ReadWrite。 如果 *UDPConnunconnected,读写方法 ReadFromUDPWriteToUDP

主要的差异在写操作上。读操作如果使用混乱,不会影响读操作本身,但一些参数细节上要注意:

示例:获取远程地址,conn.RemoteAddr()

unconnected,ListenUDP

 func UDPServerConnect() {// 1.解析地址laddr, err := net.ResolveUDPAddr("udp", ":9876")if err != nil {log.Fatalln(err)}​// 2.监听udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())defer udpConn.Close()​// 测试输出远程地址log.Println(udpConn.RemoteAddr())​// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())​// 测试输出远程地址log.Println(udpConn.RemoteAddr())​// 4.写data := []byte("received:" + string(buf[:rn]))wn, err := udpConn.WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())​// 测试输出远程地址log.Println(udpConn.RemoteAddr())}

测试:

 > go test -run UDPServerConnect2023/05/25 18:24:19 UDP server is listening on [::]:98762023/05/25 18:24:19 <nil>2023/05/25 18:24:32 received Go UDP program from 127.0.0.1:635832023/05/25 18:24:35 <nil>2023/05/25 18:24:35 send received:Go UDP program(23) to 127.0.0.1:635832023/05/25 18:24:35 <nil>

connected,DialUDP

 func UDPClientConnect() {// 1.建立连接raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")if err != nil {log.Fatalln(err)}udpConn, err := net.DialUDP("udp", nil, raddr)if err != nil {log.Fatalln(err)}​// 测试输出远程地址log.Println(udpConn.RemoteAddr())​// 2.写data := []byte("Go UDP program")wn, err := udpConn.Write(data) // WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())​// 测试输出远程地址log.Println(udpConn.RemoteAddr())​// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())​// 测试输出远程地址log.Println(udpConn.RemoteAddr())}

测试:

> go test -run UDPClientConnect
2023/05/25 18:24:32 127.0.0.1:9876
2023/05/25 18:24:32 send Go UDP program(14) to 127.0.0.1:9876
2023/05/25 18:24:32 127.0.0.1:9876
2023/05/25 18:24:35 received received:Go UDP program from 127.0.0.1:9876
2023/05/25 18:24:38 127.0.0.1:9876

示例:connected+WriteToUDP错误

udpConn, err := net.DialUDP("udp", nil, raddr)
wn, err := udpConn.WriteToUDP(data, raddr)

测试:

> go test -run UDPClientConnect
2023/05/25 18:27:41 127.0.0.1:9876
2023/05/25 18:27:41 write udp 127.0.0.1:52787->127.0.0.1:9876: use of WriteTo with pre-connected connection

示例:unconnected+Write错误

udpConn, err := net.ListenUDP("udp", laddr)
wn, err := udpConn.Write(data)

测试:

write udp [::]:9876: wsasend: A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.

Read的使用尽量遵循原则,但语法上可以混用,内部有兼容处理。

对等服务端和客户端

函数

func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error)

可以直接返回UDPConn,是unconnected状态。在编程时,我们的客户端和服务端都可以使用该函数建立UDP连接。而不是一定要在客户端使用DialUDP函数。

这样创建的客户端和服务端时对等的关系。适用于例如广播类的网络通信项目中。

示例代码:

server:

func UDPServerPeer() {// 1.解析地址laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")if err != nil {log.Fatalln(err)}// 2.监听udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())defer udpConn.Close()// 远程地址raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789")if err != nil {log.Fatalln(err)}// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())// 4.写data := []byte("received:" + string(buf[:rn]))wn, err := udpConn.WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())
}

client:

func UDPClientPeer() {// 1.解析地址laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:6789")if err != nil {log.Fatalln(err)}// 2.监听udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())defer udpConn.Close()// 远程地址raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:9876")if err != nil {log.Fatalln(err)}// 2.写data := []byte("Go UDP program")wn, err := udpConn.WriteToUDP(data, raddr)if err != nil {log.Fatalln(err)}log.Printf("send %s(%d) to %s\n", string(data), wn, raddr.String())// 3.读buf := make([]byte, 1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}log.Printf("received %s from %s\n", string(buf[:rn]), raddr.String())
}

测试:

> go test -run UDPServerPeer
2023/05/25 19:08:34 UDP server is listening on 127.0.0.1:9876
2023/05/25 19:08:46 received Go UDP program from 127.0.0.1:6789
2023/05/25 19:08:46 send received:Go UDP program(23) to 127.0.0.1:6789
> go test -run UDPClientPeer
2023/05/25 19:08:46 UDP server is listening on 127.0.0.1:6789
2023/05/25 19:08:46 send Go UDP program(14) to 127.0.0.1:9876
2023/05/25 19:08:46 received received:Go UDP program from 127.0.0.1:9876

多播编程

多播(Multicast)方式的数据传输是基于 UDP 完成的。与 UDP 服务器端/客户端的单播方式不同,区别是,单播数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机。换言之,采用多播方式时,可以同时向多个主机传递数据。

多播的特点如下:

  • 多播发送端针对特定多播组

  • 发送端发送 1 次数据,但该组内的所有接收端都会接收数据

  • 多播组数可以在 IP 地址范围内任意增加

如图所示:

image.png

多播组是 D 类IP地址(224.0.0.0~239.255.255.255):

  • 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;

  • 224.0.1.0~224.0.1.255是公用组播地址,Internetwork Control Block;

  • 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;

  • 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效

Go的标准库net支持多播编程,主要的函数:

func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error)

用于创建多播的UDP连接。

示例:多播通信

接收端端

// 多播接收端
func UDPReceiverMulticast() {// 1.多播监听地址address := "224.1.1.2:6789"gaddr, err := net.ResolveUDPAddr("udp", address)if err != nil {log.Fatalln(err)}// 2.多播监听udpConn, err := net.ListenMulticastUDP("udp", nil, gaddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())// 3.接受数据// 循环接收buf := make([]byte, 1024)for {rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Println(err)}log.Printf("received \"%s\" from %s\n", string(buf[:rn]), raddr.String())}}

发送端:

// 多播的发送端
func UDPSenderMulticast() {// 1.建立UDP多播组连接address := "224.1.1.2:6789"raddr, err := net.ResolveUDPAddr("udp", address)if err != nil {log.Fatalln(err)}udpConn, err := net.DialUDP("udp", nil, raddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()// 2.发送内容// 循环发送for {data := fmt.Sprintf("[%s]: %s", time.Now().Format("03:04:05.000"), "hello!")wn, err := udpConn.Write([]byte(data))if err != nil {log.Println(err)}log.Printf("send \"%s\"(%d) to %s\n", data, wn, raddr.String())time.Sleep(time.Second)}
}

测试:

启动发送端:

# go test -run UDPSenderMulticast
2023/05/26 16:36:43 send "[04:36:43.976]: hello!"(22) to 224.1.1.2:6789
2023/05/26 16:36:44 send "[04:36:44.977]: hello!"(22) to 224.1.1.2:6789
2023/05/26 16:36:45 send "[04:36:45.979]: hello!"(22) to 224.1.1.2:6789
2023/05/26 16:36:46 send "[04:36:46.980]: hello!"(22) to 224.1.1.2:6789

启动多个接收端,也可以在过程中继续启动:

# go test -run UDPReceiverMulticast
2023/05/26 16:36:00 UDP server is listening on 0.0.0.0:6789
2023/05/26 16:36:00 received "[04:36:43.499]: hello!" from 192.168.50.130:56777
2023/05/26 16:36:01 received "[04:36:43.500]: hello!" from 192.168.50.130:56777
2023/05/26 16:36:02 received "[04:36:43.500]: hello!" from 192.168.50.130:56777
# go test -run UDPReceiverMulticast
2023/05/26 16:36:00 UDP server is listening on 0.0.0.0:6789
2023/05/26 16:36:00 received "[04:36:43.499]: hello!" from 192.168.50.130:56777
2023/05/26 16:36:01 received "[04:36:44.500]: hello!" from 192.168.50.130:56777
2023/05/26 16:36:02 received "[04:36:45.500]: hello!" from 192.168.50.130:56777

附:Goland远程开发步骤截图:

  • 建立ssh连接

  • 打开项目

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

广播编程

广播地址,Broadcast,指的是将消息发送到在同一广播网络上的每个主机。

例如对于网络:

# ip a
ens33: <BROADCAST,MULTICAST,UP,LOWER_UP>
inet 192.168.50.130/24 brd 192.168.50.255

来说,IP ADDR 就是 192.168.50.130/24, 广播地址就是 192.168.50.255

意味着,只要发送数据包的目标地址(接收地址)为192.168.50.255时,那么该数据会发送给该网段上的所有计算机。

如图:

image.png

编码实现:

编码时数据发的发送端,同样使用 ListenUDP 方法建立UDP连接,调用WriteToUDP完成数据的发送。就是上面的对等服务端和客户端模式。

接收端:

// 广播接收端
func UDPReceiverBroadcast() {// 1.广播监听地址laddr, err := net.ResolveUDPAddr("udp", ":6789")if err != nil {log.Fatalln(err)}// 2.广播监听udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())// 3.接收数据// 4.处理数据buf := make([]byte, 1024)for {rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Println(err)}log.Printf("received \"%s\" from %s\n", string(buf[:rn]), raddr.String())}
}

发送端:

// 广播发送端
func UDPSenderBroadcast() {// 1.监听地址// 2.建立连接laddr, err := net.ResolveUDPAddr("udp", ":9876")if err != nil {log.Fatalln(err)}udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())// 3.发送数据// 广播地址rAddress := "192.168.50.255:6789"raddr, err := net.ResolveUDPAddr("udp", rAddress)if err != nil {log.Fatalln(err)}for {data := fmt.Sprintf("[%s]: %s", time.Now().Format("03:04:05.000"), "hello!")// 广播发送wn, err := udpConn.WriteToUDP([]byte(data), raddr)if err != nil {log.Println(err)}log.Printf("send \"%s\"(%d) to %s\n", data, wn, raddr.String())time.Sleep(time.Second)}
}

测试:

接收端:

# go test -run UDPReceiverBroadcast
2023/06/01 17:13:27 UDP server is listening on [::]:6789
2023/06/01 17:13:34 received "[05:13:34.707]: hello!" from 192.168.50.130:9876
2023/06/01 17:13:35 received "[05:13:35.709]: hello!" from 192.168.50.130:9876

发送端:

# go test -run UDPSenderBroadcast
2023/06/01 17:13:34 UDP server is listening on [::]:9876
2023/06/01 17:13:34 send "[05:13:34.707]: hello!"(22) to 192.168.50.255:6789
2023/06/01 17:13:35 send "[05:13:35.709]: hello!"(22) to 192.168.50.255:6789

文件传输案例

案例说明

UDP协议在传输数据时,由于不能保证稳定性传输,因此比较适合多媒体通信领域,例如直播、视频、音频即时播放,即时通信等领域。

本案例使用文件传输为例。

客户端设计:

  • 发送文件mp3(任意类型都ok)

  • 发送文件名

  • 发送文件内容

服务端设计:

  • 接收文件

  • 存储为客户端发送的名字

  • 接收文件内容

  • 写入到具体文件中

编码实现

客户端:

// 文件传输(上传)
func UDPFileClient() {// 1.获取文件信息filename := "./data/Beyond.mp3"// 打开文件file, err := os.Open(filename)if err != nil {log.Fatalln(err)}// 关闭文件defer file.Close()// 获取文件信息fileinfo, err := file.Stat()if err != nil {log.Fatalln(err)}//fileinfo.Size(), fileinfo.Name()log.Println("send file size:", fileinfo.Size())// 2.连接服务器raddress := "192.168.50.131:5678"raddr, err := net.ResolveUDPAddr("udp", raddress)if err != nil {log.Fatalln(err)}udpConn, err := net.DialUDP("udp", nil, raddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()// 3.发送文件名if _, err := udpConn.Write([]byte(fileinfo.Name())); err != nil {log.Fatalln(err)}// 4.服务端确认buf := make([]byte, 4*1024)rn, err := udpConn.Read(buf)if err != nil {log.Fatalln(err)}// 判断是否为文件名正确接收响应if "filename ok" != string(buf[:rn]) {log.Fatalln(errors.New("server not ready"))}// 5.发送文件内容// 读取文件内容,利用连接发送到服务端// file.Read()i := 0for {// 读取文件内容rn, err := file.Read(buf)if err != nil {// io.EOF 错误表示文件读取完毕if err == io.EOF {break}log.Fatalln(err)}// 发送到服务端if _, err := udpConn.Write(buf[:rn]); err != nil {log.Fatalln(err)}i++}log.Println(i)// 文件发送完成。log.Println("file send complete.")// 等待的测试time.Sleep(2 * time.Second)
}

服务端:

// UDP文件传输
func UDPFileServer() {// 1.建立UDP连接laddress := ":5678"laddr, err := net.ResolveUDPAddr("udp", laddress)if err != nil {log.Fatalln(err)}udpConn, err := net.ListenUDP("udp", laddr)if err != nil {log.Fatalln(err)}defer udpConn.Close()log.Printf("%s server is listening on %s\n", "UDP", udpConn.LocalAddr().String())// 2.接收文件名,并确认buf := make([]byte, 4*1024)rn, raddr, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}filename := string(buf[:rn])if _, err := udpConn.WriteToUDP([]byte("filename ok"), raddr); err != nil {log.Fatalln(err)}// 3.接收文件内容,并写入文件// 打开文件(创建)file, err := os.Create(filename)if err != nil {log.Fatalln(err)}defer file.Close()// 网络读取i := 0for {// 一次读取rn, _, err := udpConn.ReadFromUDP(buf)if err != nil {log.Fatalln(err)}// 写入文件if _, err := file.Write(buf[:rn]); err != nil {log.Fatalln(err)}i++log.Println("file write some content", i)}
}

测试,将文件从win传输到linux(centos)中。

上传成功,但文件内容未完整接收,注意这个UDP内容传输的特点(劣势)

# ll
total 16344
-rw-r--r--. 1 root root 9954453 Jun  2 18:08 Beyond.mp3# ll
total 16344
-rw-r--r--. 1 root root 9757845 Jun  2 18:14 Beyond.mp3

对比源文件大小:

> go test -run UDPFileClient
2023/06/02 18:14:54 send file size: 10409109

小结

  • UDP,User Datagram Protocol,用户数据报协议,是一个简单的面向数据报(package-oriented)的传输层协议

  • 单播,点对点

  • 多播,组内,使用多播(组播)地址

  • 广播,网段内,使用广播地址

  • udp连接

    • connected, net.DialUDP, Read, Write

    • unconnected, net.ListenUDP, ReadFromUDP, WriteToUDP

  • 场景:

    • 实时性要求高

    • 完整性要求不高

相关文章:

UDP程序设计

UDP协议概述 UDP&#xff0c;User Datagram Protocol&#xff0c;用户数据报协议&#xff0c;是一个简单的面向数据报(package-oriented)的传输层协议&#xff0c;规范为&#xff1a;RFC 768。 UDP提供数据的不可靠传递&#xff0c;它一旦把应用程序发给网络层的数据发送出去…...

计算机网络—电路、分组、报文交换—图文详解

计算机网络—电路、分组、报文交换 计算机网络中的数据传输方式可以根据数据的处理方式和网络资源的使用方式分为电路交换、分组交换和报文交换三种类型。 这些方式在网络设计和数据传输过程中起到了不同的作用和效果。 1. 电路交换&#xff08;Circuit Switching&#xff0…...

linux下交叉编译licensecc

本文章只做个人笔记用 下载地址&#xff1a; #https://github.com/open-license-manager/licensecc.git #下面地址下不下来就是用第一个去官网下载git clone --recursive https://github.com/open-license-manager/licensecc.git 编译前准备3个库&#xff1a;openssl&#x…...

模型剪枝综述

目录 1 深度神经网络的稀疏性&#xff1a; 2 剪枝算法分类&#xff1a; 3 具体的剪枝方法包括&#xff1a; 4 剪枝算法流程&#xff1a; 5 几种常见的剪枝算法&#xff1a; 6 结构化剪枝和非结构化剪枝各有其优缺点&#xff1a; 7 剪枝算法对模型精度的影响 8 影响剪枝…...

破解监控难题,局域网电脑监控软件哪家强?

现在的环境&#xff0c;企业要想茁壮成长&#xff0c;员工的高效工作那可是关键中的关键。但不少老板都发现了一个头疼的问题&#xff0c;员工上班老是偷懒&#xff0c;这可怎么行&#xff1f;今天&#xff0c;就来给大家详细说道说道几款出色的局域网电脑监控软件&#xff0c;…...

Linux--Socket编程TCP

前文&#xff1a;Socket套接字编程 TCP的特点 面向连接&#xff1a;TCP 在发送数据之前&#xff0c;必须先建立连接。可靠性&#xff1a;TCP 提供了数据传输的可靠性。面向字节流&#xff1a;TCP 是一个面向字节流的协议&#xff0c;这意味着 TCP 将应用程序交下来的数据看成是…...

Android Studio导入源码

在有源码并且编译环境可用的情况下&#xff1a; 1.生成导入AS所需的配置文件 在源码的根目录执行以下命令&#xff1a; source build/ensetup.sh lunch 要编译的项目 make idegen //这一步会生成out/host/linux-x86/framework/idegen.jar development/tools/idegen/idegen.sh…...

UE5 UE4 使用python进行编辑器操作

使用UE 4.25以上版本后&#xff0c;python代码改动相对较少。 如下类库在4.20/21/22等早起版本不适用&#xff0c;建议查询UE的python文档 unreal.EditorAssetLibrary 1.获取当前选中的资源&#xff08;Content中&#xff09; # 获取当前选中的资产selected_assets unreal.E…...

区块链技术在智能城市中的创新应用探索

随着全球城市化进程的加速和信息技术的快速发展&#xff0c;智能城市成为了未来城市发展的重要方向。在智能城市建设中&#xff0c;区块链技术作为一种去中心化、安全和透明的分布式账本技术&#xff0c;正逐渐展现出其在优化城市管理、提升公共服务和增强城市安全性方面的潜力…...

解决mysql事件调度器重启服务后自动失效的问题

前段时间为通过mysql事件生成测试数据&#xff0c;今天发现数据在10:57后停止了CREATE EVENT IF NOT EXISTS insert_random_data ON SCHEDULE EVERY 10 SECOND DO INSERT INTO test (createtime, random_number) VALUES (NOW(), FLOOR(RAND() * 100));检查事件状态&#…...

mybatis开启二级缓存

例子 mybatis-config.xml <configuration><settings><setting name"cacheEnabled" value"true"/></settings><environments default"development"><environment id"development"><transacti…...

Oracle大型数据库管理(一)Oracle大型数据库管理全面指南

文章目录 Oracle大型数据库管理全面指南引言1. Oracle数据库概述1.1 什么是Oracle数据库1.2 Oracle数据库的主要特点 2. Oracle数据库部署2.1 安装前的准备工作2.1.1 操作系统要求2.1.2 硬件要求2.1.3 软件环境要求 2.2 Oracle数据库的安装2.2.1 用户和目录的创建2.2.2 安装Ora…...

Arcgis中查找空间距离范围内字段相等的数据

背景 目前有两份空间点数据&#xff0c;需要通过点数据1查找100米空间距离范围内点数据2中与点数据1某个字段相同的数据 步骤 1、arcgis中加载数据 2、空间连接 结果&#xff0c;从下面这两个字段可以看出&#xff0c;点数据1在100米空间距离范围内有多个点数据2 3、选择数…...

js中map属性

JavaScript中的Map对象保存键值对&#xff0c;并且能够记住键的原始插入顺序 以下是关于如何在JavaScript中使用Map对象的博客文章概要&#xff1a; 一、创建和初始化Map对象 使用new Map()构造函数可以创建一个新的Map对象。你还可以在构造函数中传入一个可迭代对象&#x…...

CS224W—03 GNN

CS224W—03 GNN 回顾 快速回顾一下上一讲的内容。我们学到的关键概念是节点嵌入&#xff08;Node Embedding&#xff09;。我们的直觉是将网络中的节点编码到低维向量空间中。我们希望学习一个接受输入图的函数 f f f&#xff0c;并将其嵌入到低维节点嵌入空间中。在这里&am…...

库存超卖问题解决方式

文章目录 超卖问题解决方式什么是库存超卖问题&#xff1f;乐观锁和悲观锁的定义超卖问题解决方式一、悲观锁1.jvm单机锁2.通过使用mysql的行锁&#xff0c;使用一个sql解决并发访问问题3.使用mysql的悲观锁解决4. 使用redis分布式锁来解决 二、乐观锁解决1.版本号2. CAS法&…...

30岁决心转行,AI太香了

今天是一篇老学员的经历分享&#xff0c;此时的王同学在大洋彼岸即将毕业&#xff0c;手握多家北美大厂offer&#xff0c;一片明媚。谁能想到王同学的转码之路竟始于一场裁员&#xff0c;这场访谈拉开了他的回忆。 最近总刷到一些关于转行的话题&#xff0c;很多刚毕业的同学喜…...

C#知识|文件与目录操作:目录的操作

哈喽,你好啊,我是雷工! 前边学习了文件的删除、复制、移动,接下来学习目录的操作。 以下为学习笔记。 01 效果演示 1.1、显示指定目录下的所有文件 在左侧的文本框中显示出F:\F004-C#目录下的所有文件, 演示效果: 1.2、显示指定目录下的所有子文件 在左侧的文本框中显…...

从零到一:用Go语言构建你的第一个Web服务

使用Go语言从零开始搭建一个Web服务&#xff0c;包括环境搭建、路由处理、中间件使用、JSON和表单数据处理等关键步骤&#xff0c;提供丰富的代码示例。 关注TechLead&#xff0c;复旦博士&#xff0c;分享云服务领域全维度开发技术。拥有10年互联网服务架构、AI产品研发经验、…...

塔子哥的环游之旅-腾讯2023笔试(codefun2000)

题目链接 塔子哥的环游之旅-腾讯2023笔试(codefun2000) 题目内容 塔子哥是一位热衷旅游的程序员。他所在的国家共有 n 个城市,编号从 1 到 n。这些城市之间有 m 条双向的交通线路,分别为飞机线路和火车线路。塔子哥起始位于编号为 1 的城市,他计划前往编号为 n 的城市进行旅游…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

DAY 26 函数专题1

函数定义与参数知识点回顾&#xff1a;1. 函数的定义2. 变量作用域&#xff1a;局部变量和全局变量3. 函数的参数类型&#xff1a;位置参数、默认参数、不定参数4. 传递参数的手段&#xff1a;关键词参数5 题目1&#xff1a;计算圆的面积 任务&#xff1a; 编写一…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

AWS vs 阿里云:功能、服务与性能对比指南

在云计算领域&#xff0c;Amazon Web Services (AWS) 和阿里云 (Alibaba Cloud) 是全球领先的提供商&#xff0c;各自在功能范围、服务生态系统、性能表现和适用场景上具有独特优势。基于提供的引用[1]-[5]&#xff0c;我将从功能、服务和性能三个方面进行结构化对比分析&#…...