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

  • 场景:

    • 实时性要求高

    • 完整性要求不高

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式16-代理模式

设计模式16-代理模式 动机定义与结构模式定义结构 代码推导特点应用总结实例说明1. 远程代理2. 虚拟代理3. 保护代理4. 智能引用代理 动机 在面向对象系统中有一些对象由于某种原因比如对象创建的开销很大或者某些操作需要安全控制&#xff0c;或者需要进程外的访问等情况。直…

Java面试八股之Spring如何解决循环依赖

Spring如何解决循环依赖 在Spring框架中&#xff0c;循环依赖问题通常发生在两个或多个Bean相互依赖的情况下。Spring为了解决循环依赖问题&#xff0c;采用了不同的策略&#xff0c;这些策略主要取决于Bean的作用域以及依赖注入的方式。下面是一些关键点&#xff1a; 单例Be…

centos安装python 3.9

centos安装python 3.9 1. 准备工作 安装必要的构建工具和依赖项&#xff1a; sudo yum groupinstall "Development Tools" sudo yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel …

轻量级服务器资源监控平台Beszel

什么是 Beszel &#xff1f; Beszel 是一个轻量级平台&#xff0c;借助 Beszel&#xff0c;可以访问 CPU 和内存使用情况的历史数据&#xff0c;以及 Docker 容器指标&#xff08;例如特定于容器的 CPU 和内存统计信息&#xff09;。还能收到针对潜在问题的可自定义警报通知&am…

【Golang 面试 - 进阶题】每日 3 题(八)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

基于Django与spark的国漫推荐系统

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍每文一语 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主 项目介绍 近年来&#xff0c;随着互联网的蓬勃发展&#xff0c;企事业单位对信息的管理提…

C#知识|文件与目录操作:对象的创建、保存、读取

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 面向对象编程的特点就是一切皆对象&#xff0c;操作的也是对象&#xff0c;本节学习文件与目录操作中&#xff0c;对象的保存&#xff1b; 以下为学习笔记。 01 对象的特点 ①&#xff1a;对象运行在内存中&#xff…

二刷代码随想录训练营Day 11| 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素、总结

1.逆波兰表达式 题目链接/文章讲解/视频讲解&#xff1a;代码随想录 代码&#xff1a; class Solution { public:int evalRPN(vector<string>& tokens) {stack<long long> st;for(int i 0; i < tokens.size(); i){if(tokens[i] "" || tokens[i…

vue里给img的src绑定数据失效

起因 在v-for遍历数据时想要通过给img的src单向绑定 图片路径时出现问题 解决过程 上网查说是webpack构建时识别不到&#xff0c;直接不单绑数据&#xff0c;写死试试看 解决方案 直接require导入图像文件模块

热门超声波清洗机有哪些?小型超声波清洗机推荐

在繁忙的工作和生活中&#xff0c;许多人常常会因为种种原因忽略日常的小事&#xff0c;比如忘记清洁手表、眼镜、首饰等常用物品。实际上&#xff0c;这些物品表面不仅积累了灰尘和污垢&#xff0c;特别是跟眼部朝夕相处的眼镜&#xff0c;还可能滋生各种致病细菌&#xff0c;…

【数据分享】《内蒙古省统计年鉴》2000-2023

而今天要限时免费分享的数据就是2000-2023年间出版的《内蒙古省统计年鉴》并以多格式提供免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 位于中国北部的内蒙古自治区&#xff0c;以其独特的地理和文化特性&#xff0c;成为中国经济发展的重要组成部…

京东科技集团将在香港发行与港元1:1挂钩的加密货币稳定币

据京东科技集团旗下公司京东币链科技(香港)官网信息&#xff0c;京东稳定币是一种基于公链并与港元(HKD) 1:1挂钩的稳定币&#xff0c;将在公共区块链上发行&#xff0c;其储备由高度流动且可信的资产组成&#xff0c;这些资产安全存放于持牌金融机构的独立账户中&#xff0c;通…

工具(1)—截屏和贴图工具snipaste

演示和写代码文档的时候&#xff0c;总是需要用到截图。在之前的流程里面&#xff0c;一般是打开WX或者QQ&#xff0c;找到截图工具。但是尴尬的是&#xff0c;有时候&#xff0c;微信没登录&#xff0c;而你这个时候就在写文档。为了截个图&#xff0c;还需要启动微信&#xf…

数据透视表(二)

文章目录 导入外部数据源创建数据透视表Query 工具下的数据透视表创建如何统计业绩成交情况创建组利用函数构建辅助列创建组手动创建多样分组创建组区间统计创建组按年月日统计数据透视表的多种统计方法计算字段 导入外部数据源创建数据透视表 点击数据选项卡下数据-获取外部数…

环境搭建-Windows系统搭建Docker

Windows系统搭建Docker 一、系统虚拟化1.1 启用虚拟化2.2 启用Hyper-v并开启虚拟任务 三、安装WSL3.1 检验安装3.2 安装WSL 四、Docker安装4.1 Docker安装包下载4.2 Docker安装4.3 运行docker Desktop 五、Docker配置5.1 打开Docker配置中心5.2 配置Docker国内镜像 六、使用 一…

马斯克的Memphis AI超级计算中心:全球最强AI训练集群的诞生

引言 近期&#xff0c;马斯克宣布其最新的Memphis AI超级计算中心正式启动&#xff0c;这一新闻引发了科技界的广泛关注。该中心配备了10万块液冷H100 GPU&#xff0c;成为全球最强大的AI训练集群。本文将深入探讨Memphis AI超级计算中心的建设过程、技术细节、以及其对未来人…

昇思25天学习打卡营第24天|基于MobileNetv2的垃圾分类案例:从数据准备到导出模型文件

目录 MindSpore 版本配置及相关数据集与预训练权重文件下载 基于 MindSpore 的垃圾分类数据集创建与配置 MobileNetV2 模型的构建与相关类定义 基于 MindSpore 的 MobileNetV2 模型训练与测试代码分析 基于 MobileNetV2 模型的图像推理代码分析 MobileNetV2 模型的构建、加…

C语言——设计TVM(地铁自动售票机)机软件。

输入站数&#xff0c;计算费用&#xff0c;计费规则&#xff0c;6站2元&#xff0c;7-10站3元&#xff0c;11站以上为4元。 输入钱数&#xff0c;计算找零(找零时优先找回面额大的钞票)&#xff0c;找零方式为各种面额张数&#xff0c;可识别面额&#xff1a; 100,50,20,10,5,1…

yandex图标点选验证码YOLOV8识别案例

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 某yandex图标点选验证码如下: 使用过yolov8的小伙伴可能都知道,这种直接打个标注,基本上就可以了,至于问题图片由于不能很好的切割做分类,所以干脆也做成目标…

[Bugku] web-CTF靶场详解!!!

平台为“山东安信安全技术有限公司”自研CTF/AWD一体化平台&#xff0c;部分赛题采用动态FLAG形式&#xff0c;避免直接抄袭答案。 平台有题库、赛事预告、工具库、Writeup库等模块。 ------------------------------- Simple_SSTI_1 启动环境&#xff1a; 页面提示传入参数f…