golang网络编程day3

golang网络编程day3

  • golang TCP聊天室
  • golang UDP聊天室
  • URL组成
  • golang URL编程
  • golang http编程
  • http请求方法
  • golanghttp框架

golang TCP聊天室

看懂例子把它理解好,知道实现的原理是什么,还可以自己实现出来。
忘了就看day2的讲解,里面的函数都懂,这个并不难。
首先要知道tcp聊天室的实现理论:客户端采用简单的输入输出方式进行消息的发送和接收,服务端通过广播的方式将信息发送给所有的客户端。

服务端例子

package mainimport ("fmt""net"
)var (clients    = make(map[net.Addr]net.Conn)addCh      = make(chan net.Conn)delCh      = make(chan net.Addr)messageCh  = make(chan []byte)listenAddr = "localhost:8080"
)func main() {fmt.Println("Server started on", listenAddr)listener, err := net.Listen("tcp", listenAddr)if err != nil {fmt.Println(err)return}defer listener.Close()go broadcaster()for {conn, err := listener.Accept()if err != nil {fmt.Println(err)continue}addCh <- conngo handleConn(conn)}
}func broadcaster() {for {select {case conn := <-addCh:clients[conn.RemoteAddr()] = connfmt.Println("New client:", conn.RemoteAddr())case addr := <-delCh:delete(clients, addr)fmt.Println("Client disconnected:", addr)case msg := <-messageCh:for _, conn := range clients {_, err := conn.Write(msg)if err != nil {fmt.Println(err)}}}}
}func handleConn(conn net.Conn) {defer func() {delCh <- conn.RemoteAddr()conn.Close()}()for {msg := make([]byte, 4096)n, err := conn.Read(msg)if err != nil {return}messageCh <- msg[:n]}
}

常量解读:
clients:这里客户登记使用了键值对的形式,创建了一个map来登记连接,key是客户端的地址,value是与该地址建立的连接。
addCh :net.Conn类型的通道,这个是用于实现客户端连接建立。
delCh:net.Addr类型的通道,用于实现客户端连接的删除。
messageCH :字节切片类型的通道,放要传输的数据。
listenAddr:监听的地址和端口号。

可能有这样的一个疑问:这里为什么要用通道,用通道有个好处,在并发的环境下不会产生冲突,保证了并发安全。因为是模拟聊天室,那就要考虑一时间有好多个用户接入的情况,我们在操作map的时候就必须保证并发安全,这里保证map的并发安全就使用了通道来进行实现。

main主函数逻辑:
服务器端先调用了net.Listener,先创建一个监听器,然后启动广播协程:这个协程主要是用于监听每一个通道的,等下再说这个函数的细节。然后服务器有个无限循环,因为启动监听之后,就要开始监听客户端发来的请求了,这里一般都是无限循环然后调用listener.Accept()来接收客户端的请求,一旦请求接收成功就建立了Conn,可以进行数据的收发。既然有连接建立那就要进行连接的登记,也就是for里面有个处理:addCh <- conn,这就是连接的登记,把连接送入addCh通道里面。然后启动一个处理函数协程handleConn对连接进行处理。

func broadcaster()函数逻辑
首先要明白select的性质,select是go并发编程里面的内容,与通道相关,每个case都是一个通道操作,当有case的通道操作能够执行,那就会执行通道语句,当有多个通道case有效,会随机选择一个进行执行,这里是for无限循环,那就会处理所有的通道操作。当没有通道操作的时候这个select语句就会阻塞。
所以说这个函数就是来处理所有的通道操作的。来看里面每一个通道的处理

case conn := <-addCh:clients[conn.RemoteAddr()] = conn

这个是进行登记处理,net.Conn类型里面的这个函数是登记远端的IP地址,对于服务器来说那就是用户的IP地址。conn就是这个连接,注意这里我前面说过了是采用map来登记。

case addr := <-delCh:delete(clients, addr)

这个就是连接断开,然后进行登记的删除,这个delete也是调用了map里面的一个api,删除指定的key也就是addr。

case msg := <-messageCh:for _, conn := range clients {_, err := conn.Write(msg)if err != nil {fmt.Println(err)}}

这个通道处理是进行消息广播的,这里for range clients,就是拿到每一个连接然后把msg发送到这个连接去。可以看到拿到连接后调用了conn.Write(msg)把消息写入每个用户的连接中,达到数据广播的效果。

func handleConn(conn net.Conn)的逻辑,这个函数是用来进行拿到连接后的处理的。因为同一时间要进行操作用户很多,所以要启动协程来进行处理。

func handleConn(conn net.Conn) {defer func() {delCh <- conn.RemoteAddr()conn.Close()}()for {msg := make([]byte, 4096)n, err := conn.Read(msg)if err != nil {return}messageCh <- msg[:n]}
}

defer这里是进行了模拟用户操作完后的连接关闭,所以这里就要给delCH通道发个信息,进行登记的删除,然后关闭这个conn。
for循环是进行了无限循环从连接里面读取数据,因为,msg是广播的。所以每个用户都要随时接收连接里面的数据。
这里接收完数据之后也会做一个messageCh <- msg[:n],也是再次进行消息的发送,这个消息会广播出去。

总结:把握好tcp聊天室的理论原理就是用户输入数据后,服务器负责将消息广播给每个用户。这样就能很好的理解每个操作是为了什么。
实现原理:main函数负责启动开启服务器监听,然后启动通道处理,启动用户请求处理。通道处理复杂所有通道的功能,启动用户请求处理是处理每个用户。


golang UDP聊天室

package mainimport ("fmt""net""os""strings"
)const (serverAddr = "localhost:8080"
)func main() {// 解析UDP地址addr, err := net.ResolveUDPAddr("udp", serverAddr)if err != nil {fmt.Println(err)return}// 建立UDP连接conn, err := net.ListenUDP("udp", addr)if err != nil {fmt.Println(err)return}defer conn.Close()fmt.Println("Server started on", serverAddr)// 用于保存客户端的地址clients := make(map[string]*net.UDPAddr)for {// 接收请求buf := make([]byte, 1024)n, clientAddr, err := conn.ReadFromUDP(buf)if err != nil {fmt.Println(err)continue}// 解析客户端请求msg := strings.TrimSpace(string(buf[:n]))if msg == "" {continue}fmt.Printf("Received %d bytes from %s: %s\n", n, clientAddr, msg)// 如果是新客户端,添加到客户端列表中if _, ok := clients[clientAddr.String()]; !ok {fmt.Println("New client:", clientAddr)clients[clientAddr.String()] = clientAddr}// 广播消息给其他客户端for _, addr := range clients {if addr.String() == clientAddr.String() {continue // 不发送给发送者}_, err := conn.WriteToUDP([]byte(msg), addr)if err != nil {fmt.Println(err)}fmt.Printf("Sent %d bytes to %s: %s\n", len(msg), addr, msg)}}
}

解读:
1.UDP和tcp有点区别,这个解析地址的步骤要单独写出来。解析本地地址,端口号8080,调用ResolveUDPAddr()之后会得到解析后的UDP地址UDPAddr,然后进行调用ListenUDP()监听客户端的UDP请求,返回操作数据的连接。
2.这里还是用map来进行保存客户端的地址。
我学这里的时候我会去对比tcp建立聊天室的实现,我发现udp的监听这里不是无限循环监听,这里其实是我理解错了,因为udp是无连接的,所以这里我是这么来方便理解的,相当于这里调了监听函数,就是服务器UDP开了个口子,然后客户端根本不用建立连接,只管往这个
UDPConn里面发数据就完事了。
3.所以说真正的无限循环是在直接对数据进行处理这里。
下面这个无限循环for,直接调用这个conn的读数据,从里面读udp客户端的请求,n, clientAddr, err := conn.ReadFromUDP(buf)调用这个函数,把数据读到buf中,并返回远端的IP地址。
把数据清理后赋给msg并输出。
4.下面访问键值对看这个地址原来在不在,不在就是新连接进行登记。
法消息的时候遍历这个map,这样调用func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error),可以把数据广播给登记的IP地址。这就完成了广播功能。

客户端:

package mainimport ("bufio""fmt""net""os""time"
)const (serverAddr = "localhost:8080"
)func main() {// 解析服务器地址raddr, err := net.ResolveUDPAddr("udp", serverAddr)if err != nil {fmt.Println(err)return}// 建立与服务器的连接conn, err := net.DialUDP("udp", nil, raddr)if err != nil {fmt.Println(err)return}defer conn.Close()// 从标准输入读取数据reader := bufio.NewReader(os.Stdin)fmt.Print("Enter message: ")message, _ := reader.ReadString('\n')// 向服务器发送数据_, err = conn.Write([]byte(message))if err != nil {fmt.Println(err)return}// 设置读取超时err = conn.SetReadDeadline(time.Now().Add(15 * time.Second))if err != nil {fmt.Println(err)return}// 从服务器接收数据buf := make([]byte, 1024)n, _, err := conn.ReadFromUDP(buf)if err != nil {fmt.Println(err)return}fmt.Printf("Received: %s\n", string(buf[:n]))
}

分析:
客户端也是先解析服务器ip地址
然后调用,net.DialUDP(“udp”, nil, raddr),这样就拿到了conn,相当于建立了连接。(这里注意这并不是真正意义上的连接,只是我这样形容)。
拿到这个conn用户就可以往服务器发数据了,我要发的数据全装在这个reader里面,然后调用reader的Readstring方法把内容给message。
然后调用conn的写把mes传进去。这样就完成了用户发送数据。
下面还设置了一个读取超时。15秒服务器没读就会发生错误。

从服务器接收数据用到了ReadFromUDP,读到的数据都存在了buf里面。
基本逻辑就是这样。


URL组成

专业说法:什么是URL(统一资源定位符),是一种用于定位互联网上资源的地址,它是互联网上标准资源的地址,可以用来访问网页、图片、视频或其他类型的数据。
通俗理解:你在网上点击的每个链接,都是URL,因为你点链接的过程实际上就是请求资源的过程。这个URL就是定义好的访问资源的通用方法。

一个URL通常包含以下几个部分:
1.协议(scheme):定义了访问资源的方法,比如http/https/ftp等
2.域名或IP地址(host):制定了托管资源的服务器位置
3.端口号(可选)(port):用于访问服务器上的特定服务,比如HTTP协议端口号是80,HTTPS是443。
4.路径(path):指定服务器上资源的具体位置
5.查询字符串(可选)(query):以键值对的形式提供额外参数,通常用于提交表单数据或指定资源的某种特定视图。
6.片段标识符(fragment):用于指向网页内的特定部分。

举个例子:这个例子会分析就相当于学会啥是URL了。

https://www.example.com:443/path/to/myfile.html?key1=value1&key2=value2#Section2

分析:
协议:https
域名:www.example.com
端口号443
路径/path/to/myfile.html
查询字符串:key1=value1&key2=value2
注意这里是键值对形式
片段标识符:Section2

//:是协议分隔符,用于分割协议
?是查询字符串的开始,后面跟着一系列键值对,每对键值对用&隔开。
#是片段标识符的开始,它通常用于指向网页中的特定部分。浏览器会滚动到页面中ID为该标识符的元素处。注意片段标识符不会被发送到服务器,它只客户端(i浏览器上使用)。

Golang URL编程

go的标准库net/url包提供了URL编程的实现,该包提供了一些功能,使得我们可以解析、构建和操作URL字符串,注意是字符串,我们平时点的就是字符串。

解析URL

主要用到了net/url包中的Parse函数,该函数可以将URL字符串解析为URL结构体,该结构体包含了URL的各个组成部分.

package mainimport ("fmt""net/url"
)func main() {// 解析URLu, err := url.Parse("https://www.example.com/search?q=golang#top")if err != nil {fmt.Println(err)return}// 输出URL的各个部分fmt.Println("Scheme:", u.Scheme)fmt.Println("Host:", u.Host)fmt.Println("Path:", u.Path)fmt.Println("Query:", u.Query())fmt.Println("Fragment:", u.Fragment)
}

输出的内容:

Scheme: https
Host: www.example.com
Path: /search
Query: map[q:[golang]]
Fragment: top

u就是结构体,下面这些都是它里面的属性和方法

总结url.Parse(url string) ,返回值是URL结构体和一个可能发送的错误。
然后就可以操作这个结构体。

构建URL

就是用这个URL结构体转回字符串,相当于上面逆过来。

package mainimport ("fmt""net/url"
)func main() {// 构建URLu := &url.URL{Scheme: "https",Host:   "www.example.com",Path:   "/search",}q := u.Query()q.Set("q", "golang")u.RawQuery = q.Encode()// 输出URL字符串fmt.Println(u.String())
}

解读
u.Query() 用于返回一个url.Values类型,代表URL的查询字符串,这个查询字符串就是上面介绍部分的那个。url.Values是什么类型:map[string][]string,key是字符串,value是字符串切片,这意味着每个键可以对于多个值,这是因为在URL的查询字符串中,同一个键可以有多个值,例如:
?key=value1&key=value2

q.Set(“q”, “golang”) 这个事Values类型的一个方法,用于向查询字符串中添加一个键值对"q=golang"

u.RawQuery = q.Encode()
使用Encode方法,将修改后的查询参数编码为字符串,并将其赋值给u的RawQuery字段。这样URL的查询部分就被设置为"q=golang"

代码运行输出结果:

https://www.example.com/search?q=golang

解析查询参数

要解析查询参数可以使用net/url包中的Values类型。将查询字符串作为传输传递给Values函数,然后通过Get方法来获取特定参数的值。

package mainimport ("fmt""net/url"
)func main() {// 解析查询参数values, err := url.ParseQuery("q=golang&sort=recent&limit=10")if err != nil {fmt.Println(err)return}// 获取特定参数的值q := values.Get("q")sort := values.Get("sort")limit := values.Get("limit")fmt.Println("q:", q)fmt.Println("sort:", sort)fmt.Println("limit:", limit)
}

解读:
values, err := url.ParseQuery(“q=golang&sort=recent&limit=10”) 用于解读一个URL编码格式的查询字符串。这个字符串包含三个键值对,解析的结果是values类型的,存储在遍历values中。

values.Get(“q”) 用于获取特定参数的值,里面的参数是key,返回值是value

代码运行输出

q: golang
sort: recent
limit: 10
编码和解码

要对URL字符串进行编码和解码就要用net/url包下面的QueryEscape和QueryUnescape函数,分别实现对URL字符串进行编码和解码。

package mainimport ("fmt""net/url"
)func main() {// 编码字符串encoded := url.QueryEscape("https://www.example.com/search?q=golang&sort=recent")fmt.Println("Encoded:", encoded)// 解码字符串decoded, err := url.QueryUnescape(encoded)if err != nil {fmt.Println(err)return}fmt.Println("Decoded:", decoded)
}

解读:
encoded := url.QueryEscape(“https://www.example.com/search?q=golang&sort=recent”)
用于编码字符串,对给定的字符串进行百分比编码,这个对于编码URL的查询字符串部分很有用,因为对处理URL特殊字符(& = ?)时,会把这些特殊字符转换为百分比形式。
这里的输出结果:

Encoded: https%3A//www.example.com/search%3Fq%3Dgolang%26sort%3Drecent

decoded, err := url.QueryUnescape(encoded)
解码就是进行还原

输出:
Decoded: https://www.example.com/search?q=golang&sort=recent

一个疑问:为什么要换成百分比?
将URL中的特定字符串替换为百分比,这种被定义为URL编码。
这种编码的好处:
1.保留字符:URL中某些字符有特殊含义,百分比编码可以在不改变原有意义的情况下安全地包含这些字符。
2.非ASCII字符,对于某些在ASCII中有特殊含义的字符必须使用百分比编码来标识
3.安全性:百分比编码有助于消除URL中可能引起安全问题的字符。
4.一致性和标准性:通过百分比编码可以确保URL的一致性和标准化,使得不同的网络设备和软件能够正确解析URL。


golang http编程

这种编程是通过net/http包实现的。
这个标注库提供了完善的HTTP客户端和服务器实现。所以可以在go语言实现编写HTTP相关的应用。
直接来看demo,这些都是这个包的应用

HTTP客户端
一个简单的使用net/http包发送 HTTP GET请求的例子

package mainimport ("fmt""io/ioutil""net/http"
)func main() {resp, err := http.Get("http://www.example.com/")if err != nil {fmt.Println(err)return}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println(string(body))
}

解读:
resp, err := http.Get(“http://www.example.com/”) ,调用http下的这个函数可以向这个指定的网站发送一个GET请求,这个函数非常常用于从指定的URL获取数据。
参数就是一个url,就是要请求的url,字符串类型的。
resp是*http.Response类型的指针,它标识服务器的响应。err是请求过程中可能发生的错误。

当这个函数调用成功,它会向这个URL发送一个GET请求,如果成功,resp将是服务器的响应。这个响应前面学过理论基础,就是服务器那边发来的信息。包含状态码、响应头以及响应体等信息。如果请求失败err会描述发生的错误。

*http.Response类型解读:这是一个结构体,下面注意介绍里面的字段。
Status:
类型:string
描述:响应的状态行,例如 “200 OK”。这个字段包含了状态码和状态描述。

StatusCode:
类型:int
描述:数字形式的 HTTP 状态码,例如 200、404 等。

Header:
类型:http.Header(实质上是 map[string][]string)
描述:响应头。这是一个映射表,包含了所有的响应头字段和值。每个头字段可以有一个或多个值。

Body:
类型:io.ReadCloser
描述:响应的主体。这是一个 io.Reader 接口,用于读取响应的主体内容。它还实现了 io.Closer 接口,这意味着在读取完毕后,你需要调用 Body.Close() 来关闭它,释放资源。

ContentLength:
类型:int64
描述:响应主体的长度。如果长度未知,该值为 -1。

TransferEncoding:
类型:[]string
描述:传输编码列表,按照应用的顺序。

Close:
类型:bool
描述:指示是否应在读取完响应主体后关闭连接。

Request:
类型:*http.Request
描述:生成这个响应的 HTTP 请求。这对于跟踪请求-响应链很有用。

TLS:
类型:*tls.ConnectionState
描述:如果通过 HTTPS 访问,则包含有关 TLS 连接的信息。如果不是 HTTPS,则为 nil。

通过上述描述,这个resp完全就是服务器的响应的全部内容,我们可以按照需要选择字段进行操作。这里我们一般都去关注body这个字段:响应的主体,就是我们要的主要内容。
关于这个body的数据类型做一点说明:

type ReadCloser interface {ReaderCloser
}

它是一个接口类型,它又嵌套了两个接口,一个接口用于从数据流中读数据,这个接口包含了个read方法,可以实现读取数据到指定字节切片中;另一个用于关闭数据流,它又一个close()方法,调用后关闭数据流。

这里有个注意就是处理完响应主题后,应该关闭resp.Body,这样做的原因和前面学的那些关闭几乎都一个原因,避免资源泄露。

body, err := ioutil.ReadAll(resp.Body)
用于读取响应体的数据,传的参数只要求实现了io.Reader接口就可以传,这样刚好符合。然后返回一个字节切片,这样就做到了把响应体里面的内容读出来。

综上:使用net/http包轻松的做到了向指定的URL发送http get请求,并且得到响应后输出响应的内容。

一个更进阶的用法,使用http.Client可以实现更复杂的HTTP客户端逻辑,例如设置请求头、发送POST请求等。

看例子,还是客户端

package mainimport ("bytes""fmt""io/ioutil""net/http"
)func main() {url := "http://www.example.com/login"data := []byte(`{"username": "admin", "password": "password"}`)req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))if err != nil {fmt.Println(err)return}req.Header.Set("Content-Type", "application/json")client := &http.Client{}resp, err := client.Do(req)if err != nil {fmt.Println(err)return}defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println(string(body))
}

解读
url := “http://www.example.com/login”
data := []byte({"username": "admin", "password": "password"})
url指向登录接口,下面这个json格式标识的用户名和密码。

req, err := http.NewRequest(“POST”, url, bytes.NewBuffer(data)) 这个函数用于创建一个新的HTTP请求。这个函数的作用是构造一个指定HTTP方法、URL和可选正文的HTTP请求。

结构

func NewRequest(method, url string, body io.Reader) (*http.Request, error)

参数:方法;指定url;body是请求的正文,如果请求不需要正文,可以为nil。正文是在HTTP请求和响应中,传输的主要数据部分,就是头部的下面那部分,即实际要收发的数据内容。这个正文通常也是有格式的:文本(纯文本,json,xml),表单数据,二进制数据(文件)
在上面这个代码中,我请求的正文就是一个json格式的字节切片,这里由于是要io.Reader所以这里用了一个bytes.NewBuffer(data)创建了一个io.Reader对象,从而可以作为参数传入。

返回值:*http.Request标识构造的HTTP请求,可以说是请求的要素全部齐全了,包含了方法,URL,头部,正文等信息。

总结 这个函数起到了定制请求的效果,满足了对灵活性的需求,通过这个函数我可以构造几乎任何类型的HTTP请求。

req.Header.Set(“Content-Type”, “application/json”) 设置请求的请求正文的内容格式

client := &http.Client{}
resp, err := client.Do(req)
这里是创建一个实例来发送求情,使用client结构体的Do()方法,来发送这个创建的请求,并接收响应。这个resp就是接收的响应。

最后要关闭响应体。defer resp.Body.Close()

拿到的这个响应体,可以用
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
打印出来,因为resp.Body是io.Reader类型的。

http服务器

下面是一个简单的服务器锂离子

package mainimport ("fmt""net/http"
)func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")})http.ListenAndServe(":8080", nil)
}

解读:
*http.HandleFunc(“/”, func(w http.ResponseWriter, r http.Request) {
fmt.Fprintf(w, “Hello, World!”)
})

注册处理函数:这个函数用于注册一个处理函数,该函数用于处理对特定路径的HTTP请求。
这个代码中,这个函数被用来处理所有发往根路径"/"的请求。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

pattern,字符串类型,代表指定要处理的URL路径,handler一个函数,当接收到匹配这个路径的请求时,这个函数被调用。

*func(w http.ResponseWriter, r http.Request) 匿名函数,w http.ResponseWriter用于构建和发送HTTP响应,你可以通过w写入响应正文、设置响应的状态码、添加响应头等。
*r http.Request 代表接收到的HTTP请求。它包含了请求的各自信息,比如请求方法,URL,请求头,请求正文等。

综上:也就是服务器接收到了请求,就会输出响应w和hello world

http.ListenAndServe(“:8080”, nil) 用于启动HTTP服务器,第一个参数代表监听本地的8080端口,第二个参数是处理器,nil表示使用默认多路复用器,http.DefaultServeMux。

调用之后会起到HTTP服务器,并监听端口,等待并处理HTTP请求。

http.ServeFile可以轻松地将静态文件提高给客户端。
package mainimport ("net/http"
)func main() {http.Handle("/", http.FileServer(http.Dir("static")))http.ListenAndServe(":8080", nil)
}

解读:
会把当前目录下的static文件夹作为根目录,提供静态文件服务,例如:客户端请求http://localhost:8080/index.html时,服务器返回的是static/index.html文件。

总结:

以上是http客户端和服务器的基本用法,这个包还有很多其他的功能,比如实现了WebSocket,HTTP长连接,Http代理等等。根据需要查询文档。


http请求方法

看day1

http框架

1.Gin:Gin是一个高性能、易用的HTTP框架,它提供了路由、中间件、静态文件服务、模板渲染等常用功能,并且支持自定义中间件和路由分组等高级特性。Gin的设计理念是尽量简单、快速地完成HTTP请求处理,并且提供高度可定制的API接口。

2.ECHO

3.Beego:Beego是一个完整的Web框架,它提供了MVC架构、ORM、Websocket、RESTful API等功能,并且支持国际化、日志、缓存等高级特性。Beego的设计理念是快速开发、易用可扩展,并且提供丰富的文档和社区支持。

4.Revel
还有很多框架,这里做了解,1是必须要学的,Beego看有没有多于的时间学。

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

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

相关文章

自然语言处理(NLP)—— Dialogflow ES聊天机器人

1. 背景介绍 这个实验室的目标是让你了解并使用Google的Dialogflow服务。Dialogflow是一个可以让你创建聊天机器人的服务&#xff0c;这个过程不需要或者只需要很少的编程技能。 1.1 账号的创建 为了完成这个实验室&#xff0c;你需要在以下网站上创建账号&#xff1a…

网络安全之SSL证书加密

简介 SSL证书是一种数字证书&#xff0c;遵守SSL协议&#xff0c;由受信任的数字证书颁发机构&#xff08;CA&#xff09;验证服务器身份后颁发。它具有服务器身份验证和数据传输加密的功能&#xff0c;能够确保数据在传输过程中的安全性和完整性。 具体来说&#xff0c;SSL证…

three.js CSS3DObject、CSS2DObject、CSS3DSprite、Sprite的作为标签的区别

CSS3DObject、CSS2DObject、CSS3DSprite、Sprite的作为标签的区别 是否面向相机场景缩放时&#xff0c;是否会跟随是否会被模型遮挡CSS2DObject是否否CSS3DObject否是否CSS3DSprite是是是Sprite是是是 CSS3DObject 和 CSS3DRenderer 搭配来渲染标签&#xff1b; CSS2DObject …

excel统计分析——卡方独立性检验(下)

参考资料&#xff1a;生物统计学 书接上文&#xff1a;https://blog.csdn.net/maizeman126/article/details/135893731 2、配对列联表 配对设计的数据&#xff0c;进行列联表检验时&#xff0c;采用McNemar-Bowker检验法进行检验。检验统计量为&#xff1a; 自由度dfk(k-1)/2…

部署私有知识库项目FastGPT

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景。 项目文档: [快速了解 FastGpt | FastGptFastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即…

Jmeter直连mysql数据库教程

mysql数据库能够通过Navicat等远程连接工具连接 下载驱动并加入jmeter 1.mysql驱动下载地址&#xff1a;MySQL :: Download MySQL Connector/J (Archived Versions) 找到对应的驱动下载&#xff1a;如下图&#xff1a; 把驱动jar包加入jmeter 配置jmeter连接mysql数据库…

计算机专业一定要参加蓝桥杯吗

蓝桥杯对于计算机专业的学生来说是一个非常重要的赛事&#xff0c;主要有以下几个原因&#xff1a; 提升知识和技能水平&#xff1a;蓝桥杯涵盖了计算机专业的多个方向&#xff0c;包括软件、硬件、网络等。通过参加蓝桥杯&#xff0c;学生可以将所学的知识与实际应用相结合&a…

RK3588 Android 12 Framework修改记录(八)Settings Ethernet以太网 多网卡设置静态IP

https://blog.csdn.net/lishu_/article/details/114391764 如何优雅的实现Android Ethernet 以太网的开启与关闭 RK3588 Android 12 Framework修改记录&#xff08;八&#xff09;Settings Ethernet以太网 多网卡设置静态IP - 知乎 Android 7.1 以太网反射 EthernetManager 配置…

【C语言刷题系列】喝汽水问题

文章目录 一、文章简介 1.先买再换 1.1 代码逻辑&#xff1a; 1.2 完整代码 1.3 运行结果 1.4 根据方法一总结优化 2.边买边换 2.1 代码逻辑&#xff1a; 2.2 完整代码 2.3 运行结果 一、文章简介 本文所述专栏——C语言经典编程问题 C语言刷题_倔强的石头106的博客…

Linux操作系统——管道(二) 进程池

概念层面理解进程池 比如说我们一开始有一个父进程&#xff0c;分别创建5个管道&#xff0c;5个子进程&#xff0c;这5个子进程都向管道里面进行读取&#xff0c;而我们对应的父进程&#xff0c;因为我们前面谈过管道的4种情况里面&#xff0c;有一个种情况是&#xff0c;正常…

vcruntime140.dll怎么下载安装,vcruntime140.dll详细安装教程

vcruntime140.dll’&#xff0c;这个看似陌生而又技术性的文件名&#xff0c;实际上是计算机系统中一个至关重要的动态链接库文件。它是Visual C Redistributable运行时组件的一部分&#xff0c;由微软公司开发并维护&#xff0c;对于许多基于Windows操作系统的软件正常运行起着…

在 SQL Server 中使用 SQL 语句查询不同时间范围的数据

在 SQL Server 中&#xff0c;我们经常需要从数据库中检索特定时间范围内的数据。通过合理运用 SQL 语句&#xff0c;我们可以轻松地查询今天、昨天、近7天、近30天、一个月内、上一月、本年和去年的数据。下面是一些示例 SQL 查询&#xff0c;让我们逐一了解。 查询今天的数据…

PEI是聚醚酰亚胺(Polyetherimide)在粘接使用时使用UV胶水的优势有哪些?要注意哪些事项?

在使用UV胶水进行聚醚酰亚胺&#xff08;Polyetherimide&#xff0c;PEI&#xff09;粘接时&#xff0c;有一些优势和注意事项&#xff1a; 优势&#xff1a; 1.快速固化&#xff1a; UV胶水具有快速固化的特性&#xff0c;通过紫外线光源的照射&#xff0c;可以在几秒到几分钟…

Oracle PL/SQL Programming 第5章:Iterative Processing with Loops 读书笔记

总的目录和进度&#xff0c;请参见开始读 Oracle PL/SQL Programming 第6版 本章探讨 PL/SQL 的迭代控制结构&#xff08;也称为循环&#xff09;&#xff0c;它允许您重复执行相同的代码。 PL/SQL 提供了三种不同类型的循环结构&#xff1a; 简单或无限循环FOR 循环&#x…

MQTT环境搭建

本文介绍了MQTT测试环境的搭建过程和测试时使用的命令&#xff0c;对需要使用MQTT协议的相关开发人员有一定的帮助。 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输&#xff09;。 MQTT是 ISO 标准&#xff08;ISO/IEC PRF 20922&#xf…

国标GB/T 28181详解:GB/T28181状态信息报送流程

目 录 一、状态信息报送 二、状态信息报送的基本要求 三、命令流程 1、流程图 2、流程描述 四、协议接口 五、产品说明 六、状态信息报送的作用 七、参考 在国标GBT28181中&#xff0c;定义了状态信息报送的流程&#xff0c;当源设备(包括网关、SIP 设备、SIP 客…

ThinkPHP6的助手函数汇总

原文地址 abort(): 抛出 HTTP 异常 1. /** 2. * 抛出 HTTP 异常 3. * param integer|Response $code 状态码 或者 Response 对象实例 4. * param string $message 错误信息 5. * param array $header 参数 6. */ 7. abort($code, string…

关于可变类型和不可变类型的探究

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

数据库建模之PowerDesigner创建概念模型

数据模型&#xff08;Data Model&#xff09;是数据特征的抽象&#xff0c;它从抽象层次上描述了系统的静态特征、动态行为和约束条件&#xff0c;为数据库系统的信息表示与操作提供一个抽象的框架。数据模型所描述的内容有三部分&#xff0c;分别是数据结构、数据操作和数据约…

01-16Maven-SpringBoot入门

Maven继承Maven高级SpringSpringBoot入门 Maven 一、概念及功能 概念&#xff1a;Maven是Apache软件基金会组织维护的一款专门为Java项目提供项目构建和依赖管理的工具 1.1作用&#xff1a; 项目构建 构建&#xff1a;是一个将代码从开发阶段到生产阶段的一个过程&#xf…