使用h2c包实现,h2c包即:http2 without tls,不用tls的http2协议,有关支持这个的声音早在2016年就已经提出来(issue)[https://github.com/golang/go/issues/14141]
现在已经有官方包h2c(golang.org/x/net/http2/h2c)[golang.org/x/net/http2/h2c]

服务端原本是HTTP协议,修改之后代码如下(注释部分是原来的代码)

import "golang.org/x/net/http2/h2c"

func StartServer(listenAddress *string) {
router := server.InitRouter()
// serverHttp := &http.Server{
// Addr: *listenAddress,
// Handler: router,
// }

// if err := serverHttp.ListenAndServe(); err != nil && err != http.ErrServerClosed {
// log.Fatalf("listen err: %s\n", err)
// }

serverHttp := &http.Server{
Addr: *listenAddress,
Handler: h2c.NewHandler(router, &http2.Server{}),
}

if err := serverHttp.ListenAndServe(); err != nil && err != http.ErrServerClosed {
blog.Errorf("listen err: %s\n", err)
}
}

客户端修改如下,注释部分是旧代码,新的Tansport为transport2变量:

func (m NewTaskHandler) SendTaskHttp(msg MsgTask) (err error) {
if msg.Port < 1 {
msg.Port = 6180
}
url := fmt.Sprintf("http://%v:%v/api/v1/task", msg.IP, msg.Port)
retryClient := retryablehttp.NewClient()
retryClient.RetryMax = 5
retryClient.RetryWaitMin = time.Duration(5 * time.Second)
retryClient.RequestLogHook = func(logger retryablehttp.Logger, req *http.Request, retry int) {
req.AddCookie(&http.Cookie{
Name: "Token",
Value: "TWFzdGVyLVNlY3JldCBsb2cgZmlsZW5hbWU=",
})
}
// transport := &http.Transport{
// TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 不校验服务端证书
// MaxIdleConns: 10,
// MaxIdleConnsPerHost: 10,
// IdleConnTimeout: 30 * time.Second, // 连接空闲超时
// }
transport2 := &http2.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 不校验服务端证书,
AllowHTTP: true,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
}
retryClient.HTTPClient.Transport = transport2
retryClient.HTTPClient.Timeout = 60 * time.Second

body, err := json.Marshal(msg)
if err != nil {
blog.Error("err body %v %v", msg, err)
return err
}
resp, err := retryClient.Post(url, "application/json", body)
if err != nil {
blog.Error("err url %v %v", url, err)
return err
}

以上,我使用了retryablehttp用于失败重试,可替换为默认的httpclient,可忽略这一点不同,另外我在transport2中配置:

  • AllowHTTP:true,实现允许HTTP协议,
  • DialTLS不使用默认的TLS连接,使用我们自定义的直接连接.
  • TLSClientConfig: &tls.Config{InsecureSkipVerify: true} 不校验服务端证书

使用wireshark抓包试试:

我尝试在client机器上运行tcpdump抓包,过滤协议为tcp, 网卡接口为bond0,端口6108,使用-w参数将包文件保存到package.pcap

tcpdump tcp -i bond0 port 6180 -w package.pcap

然后使用wireshark打开数据包
奇怪的是wireshark并没有显示协议类型,我查了一番应该是默认不支持显示http2协议,也许和我的版本有关

upload successful

改用Charles打开数据包,可见协议已经为HTTP2

upload successful
至此已经实现了客户端和服务端使用HTTP2协议通讯