闽公网安备 35020302035485号
package main
import (
"fmt"
"log"
"net/http"
)
// IndexHandler记录请求的基本信息: 请关注r.RemoteAddr
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Println("receive a request from:", r.RemoteAddr, r.Header)
w.Write([]byte("ok"))
}
// net/http 默认开启持久连接
func main() {
fmt.Printf("Starting server at port 8081\n")
if err := http.ListenAndServe(":8081", http.HandlerFunc(Index)); err != nil {
log.Fatal(err)
}
}
1.ListenAndServe创建了默认的httpServer服务器,go通过首字母大小写来控制访问权限,如果首字母大写,则可以被外部包访问, 类比C#全局函数、静态函数。func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
2.net/http服务器默认开启了Keep-Alive, 由Server的私有变量disableKeepAlives体现。type Server struct {
...
disableKeepAlives int32 // accessed atomically.
...
}
使用者也可以手动关闭Keep-Alive, SetKeepAlivesEnabled()会修改私有变量disableKeepAlives的值s := &http.Server{
Addr: ":8081",
Handler: http.HandlerFunc(Index),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.SetKeepAlivesEnabled(true)
if err := s.ListenAndServe(); err != nil {
log.Fatal(err)
}
以上也是go语言包的基本制作/使用风格。服务器会收到如下日志, 图中红圈处表明浏览器使用了系统随机的固定端口建立tcp连接。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
func main() {
client := &http.Client{
Timeout: 10 * time.Second,
}
for {
requestWithClose(client)
time.Sleep(time.Second * 1)
}
}
func requestWithClose(client *http.Client) {
resp, err := client.Get("http://127.0.0.1:8081")
if err != nil {
fmt.Printf("error occurred while fetching page, error: %s", err.Error())
return
}
defer resp.Body.Close()
c, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Couldn't parse response body. %+v", err)
}
fmt.Println(string(c))
}
服务器收到的请求日志如下:
图中红框显示httpclient使用固定端口61799发起了http请求,客户端/服务器维持了HTTP Keep-alive。
使用netstat -an | grep 127.0.0.1:8081可围观系统针对特定ip的TCP连接:
客户端系统中针对 服务端也只建立了一个tcp连接,tcp连接的端口是61799,与上文呼应。
使用Wireshark查看localhost网卡发生的tcp连接Image
// 下面的代码没有读完body,导致Keep-alive失效
func requestWithClose(client *http.Client) {
resp, err := client.Get("http://127.0.0.1:8081")
if err != nil {
fmt.Printf("error occurred while fetching page, error: %s", err.Error())
return
}
defer resp.Body.Close()
//_, err = ioutil.ReadAll(resp.Body)
fmt.Println("ok")
}
此次服务端日志如下:
上图红框显示客户端持续使用新的随机端口建立了TCP连接。
查看客户端系统建立的tcp连接:
Wireshark抓包结果:
图中红框显示每次HTTP请求/响应 前后均发生了三次握手、四次挥手。