大家好,针对Go语言 net/http
标准库,将梳理的相关知识点分享给大家~~
围绕 net/http
标准库相关知识点还有许多章节,请大家多多关注。
文章中代码案例只有关键片段,完整代码请查看github
仓库:https://github.com/hltfaith/go-example/tree/main/net-http
本章节案例,请大家以 go1.16+
版本以上进行参考。
net/http标准库系列文章
- Golang net/http标准库常用请求方法(一)
- Golang net/http标准库常用方法(二)
本节内容
- HandleFunc() 方法
- ListenAndServe() 方法
- ListenAndServeTLS() 方法
- FileServer()方法
- FileServerFS()方法
ListenAndServe()、HandleFunc()
ListenAndServer()
函数顾名思义是:监听 TCP
端口和服务。主要作用是使用处理程序调用服务来处理传入连接上的请求,接受的连接被配置为启用TCP保活。
ListenAndServer()
函数,会返回非 nil
的错误。
函数原型
go
func ListenAndServe(addr string, handler Handler) error
HandleFunc()
函数,主要作用是在DefaultServeMux
全局锁中注册指定的路由处理程序。
函数原型
go
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
http服务创建监听,代码片段。
这是一个非常简单代码片段,首先新增了路由,并对请求响应返回一段字符串,最后监听8080
端口等待请求连接。
注:代码中
:8080
代表监听本地所有网卡,都可以访问到该服务。其次是默认将ipv4
和ipv6
地址全部监听, 这种http.ListenAndServe("0.0.0.0:8080", nil)
写法是和http.ListenAndServe(":8080", nil)
一样的。
结合上面代码片段,我们来分析一下 /hello
路由服务是如何监听到本地的。
首先,先分析第一个问题: http.ListenAndServe()
如何将url路由关联起来的,我们看到代码中ListenAndServe(":8080", nil)
第二个参数 nil 代表是0零值,并没有携带处理程序调用的服务,那它怎么与 http.HandleFunc()
关联起来的?
ListenAndServe() 函数源码中的解释是:如果 Handler
参数为nil 值时则使用 DefaultServeMux
全局锁。
下面我们再来看看到底在什么环节,判断 Handler
参数为nil 值时则使用 DefaultServeMux
类型的。
调用过程:
http.ListenAndServe()
->(srv *Server) ListenAndServe()
->(srv *Server) Serve(l net.Listener)
->go c.serve(connCtx)
最终 net/http 库会针对每个请求的客户端,通过 go c.serve(connCtx)
协程针对每次http请求单独处理。
go
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
下图代码中就进行判断 handler
是不是nil
值,如果是则使用 DefaultServeMux
所维护的 Handler 路由服务进行处理。
接着,我们来分析第二个问题: http.ListenAndServe()
如何进行端口监听的呢?
http.ListenAndServe()
先是通过 net.Listen("tcp", addr)
收听本地网络地址上的广播, 然后拿到监听描述符,最后一直监听 Accept()
的请求连接。
ListenAndServeTLS()
ListenAndServeTLS()
函数是基于 ListenAndServe()
函数的基础上增加了安全证书,也是我们说的HTTPS
。
这里在简单说明下HTTP
与HTTPS
的区别:
(1) HTTP
是超文本传输协议,信息是明文传输,HTTPS
则是具有安全性的SSL
加密传输协议。
(2) HTTP
和HTTPS
使用的是完全不同的连接方式,使用的端口也不一样,前者是80,后者是443。
(3) HTTP
的连接很简单,是无状态的。
(4) HTTPS
协议是由SSL+HTTP
协议构建的可进行加密传输、身份认证的网络协议,要比HTTP
协议安全。
ListenAndServeTLS()
函数原型
go
func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error
参数一 addr
:监听网卡的IP地址与端口,如果值是空字符默认则是443端口。
参数二 certFile
:证书文件,可以通过 crypto/tls
库去生成。(或者通过OpenSSL签发证书)
参数三 keyFile
:证书Key
参数四 handler
:和ListenAndServe()
函数中的作用一致,是处理http路由服务处理程序。
函数使用
listenandservetls.go
go
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "Hello, TLS!\n")
})
log.Fatal(http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil))
}
需要说明下,这里的 cert.pem
、key.pem
证书与key文件是通过 crypto/tls
库中的 generate_cert.go
提供的代码块生成。
生成cert证书用法
shell
macbookpro:net mac$ go run /usr/local/go/src/crypto/tls/generate_cert.go -host 127.0.0.1
2024/05/12 17:31:15 wrote cert.pem
2024/05/12 17:31:15 wrote key.pem
我这里的GOROOT
路径是 /usr/local/go
,大家可以通过 go env | grep GOROOT
命令查看具体的路径。
-host 127.0.0.1
参数:代表证书仅在本地环境测试使用。
下面,我们运行程序后,在浏览器访问 https://127.0.0.1
地址,发现浏览器警告这是一个不安全的连接,因为我们的证书没经过受信任机构处理。
下面将所生成的证书加入到系统受信任区域,我所使用的是mac系统,下面操作根据系统步骤不太一样,大家可以根据系统自行搜索下。
首先找到我们的证书路径
操作完上面的配置后,我们在浏览器访问 https://127.0.0.1
地址。(务必要先重启浏览器)
浏览器告诉我们,这次连接访问是安全的。
在浏览器开发调试模式下,也可以看到证书是安全的。
FileServer()
FileServer() 函数创建一个静态文件服务。
函数使用
fileserver.go
go
func main() {
log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("."))))
}
代码中把当前目录设置为根目录,本目录下的所有文件将展示在web页面中
在浏览器中访问 http://127.0.0.1:8080
地址,可以看到本地目录中的所有文件和目录都可以被访问到。
FileServerFs()
FileServerFS()
函数,使用文件系统的内容为HTTP请求提供服务的处理程序。
注:该函数是 go1.22.0 版本新增的特性。
函数使用
fileserverfs.go
go
func main() {
filename := "index.html"
contents := []byte("<h1>帽儿山的枪手</h1>")
fsys := fstest.MapFS{
filename: {Data: contents},
}
http.Handle("/", http.FileServerFS(fsys))
log.Fatal(http.ListenAndServe(":8080", nil))
}
代码块中封装了html格式的文件内容,然后通过 FileServerFS()
函数发布服务。
技术文章持续更新,请大家多多关注呀~~
搜索微信公众号,关注我【 帽儿山的枪手 】
参考材料