先简单的贴出服务端和客户端的核心代码:

客户端:session,err:=quic.DialAddr(addr,tlsConf,nil)

服务端:listener,err:=quic.ListenAddr(addr,generateTLSConfig(),nil)

1.客户端

tlsConf := &tls.Config{

InsecureSkipVerify: true,

NextProtos:         []string{"quic-echo-example"},

}

首先在QUIC配置TLS来保证安全性。

session,err:=quic.DialAddr(addr,tlsConf,nil)

拨号,即连接指定的服务器端IP地址。session类似于TCP/IP的套接字。

stream,err:= ession.OpenStreamSync(context.Background())

创建流,在stream上发送和接收信息。context.Background()类似于管道,相当于给予QUIC一个通信的手段。

//发送数据

stream.Write([]byte(message))

//接收数据

buf:=make([]byte,len(message))

io.ReadFull(stream,buf)

2.服务端

listener,err:=quic.ListenAddr(addr,generateTLSConfig(),nil)

监听addr,generateTLSConfig()代表TLS的配置,最后一个参数是quic.config一般是nil。

sess,err:=listener.Accept(context.Background())

sess与上面的session类似。

stream,err:=sess.AcceptStream(context.Background())

创建stream,接收信息,在server端新建一个context专门对这个连接进行通信。

3.仿照用例自己实现

分析完上面主要的代码之后,我们现在自己实现一个简单的QUIC通信,实现客户端发送Hello,服务端发送Hi。为了方便,我们把客户端和服务端写到一个文件夹里。

const addr="localhost:6688"

const clientMessage="Hello"

const serverMessage= Hi"

首先定义一下本地监听端口号和要发送的数据:

3.1客户端

session,err:=quic.DialAddr(addr,tlsConf,nil)

为了简单,我们使用最简单的TLS配置安全传输

stream,err:=session.OpenStreamSync(context.Background())

创建个流,使用流传输数据

_,err=stream.Write([]byte(clientMessage))

客户端发送数据

_,err=stream.Read(buf)

读取服务端发来的信息到buf,buf是一个字节数组。客户端至此完成。

3.2服务端

listener,err:=quic.ListenAddr(addr,generateTLSConfig(),nil)

服务端使用TLS检测安全,generateTLSConfig()类似于通用配置,可自定义。

sess,err:=listener.Accept(context.Background())

接收新的连接请求。

stream,err:=sess.AcceptStream(context.Background())

在刚才的连接请求上创建新的接收流。

_,err=stream.Read(buf)

_,err=stream.Write([]byte(serverMessage))

发送和接收数据。

以上完整代码如下:

package main

import (

"context"

"crypto/rand"

"crypto/rsa"

"crypto/tls"

"crypto/x509"

"encoding/pem"

"fmt"

"github.com/lucas-clemente/quic-go"

"math/big"

"time"

)

const addr = "localhost:4242"

const clientMessage = "Hello"

const serverMessage = "Hi"

func main() {

go func() {

err := server()

if err != nil {

panic(err)

}

}()

err := client()

if err != nil {

panic(err)

}

// 等待main和go程 执行完,防止server执行完自动结束

time.Sleep(time.Second * 5)

}

// 客户端

func client() error {

tlsConf := &tls.Config{

InsecureSkipVerify: true,

NextProtos:         []string{"quic-echo-example"},

}

session, err := quic.DialAddr(addr, tlsConf, nil)

if err != nil {

return err

}

stream, err := session.OpenStreamSync(context.Background())

if err != nil {

return err

}

fmt.Printf("Client: Sending '%s'\n", clientMessage)

_, err = stream.Write([]byte(clientMessage))

if err != nil {

return err

}

buf := make([]byte, 1024)

_, err = stream.Read(buf)

if err != nil {

return err

}

fmt.Printf("Client: Got '%s'\n", buf)

return nil

}

// 服务端

func server() error {

listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)

if err != nil {

return err

}

sess, err := listener.Accept(context.Background())

if err != nil {

return err

}

stream, err := sess.AcceptStream(context.Background())

if err != nil {

panic(err)

}

buf := make([]byte, 1024)

_, err = stream.Read(buf)

if err != nil {

return err

}

fmt.Printf("Server: Got '%s'\n", buf)

fmt.Printf("Server: Sending '%s'\n", serverMessage)

_, err = stream.Write([]byte(serverMessage))

return err

}

func generateTLSConfig() *tls.Config {

key, err := rsa.GenerateKey(rand.Reader, 1024)

if err != nil {

panic(err)

}

template := x509.Certificate{SerialNumber: big.NewInt(1)}

certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)

if err != nil {

panic(err)

}

keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})

certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)

if err != nil {

panic(err)

}

return &tls.Config{

Certificates: []tls.Certificate{tlsCert},

NextProtos:   []string{"quic-echo-example"},

}

}