本文主要是介绍Golang使用Quic-Go开源库实现Quic客户端和服务端,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Quic-Go介绍
Quic-Go是Go语言Quic协议(RFC 9000、RFC 9001、RFC 9002)的实现。它支持HTTP/3(RFC 9114),包括QPACK(RFC 9204)和HTTP数据报(RFC 9297)。
- Github地址
https://github.com/quic-go/quic-go
- 下载Quic-Go开源库
go get -u github.com/quic-go/quic-go
- 下述代码中Go版本和Quic-Go版本
- go version go1.22.6 linux/amd64
- github.com/quic-go/quic-go v0.46.0
Quic客户端代码
package mainimport ("context""crypto/tls""fmt""log""strconv""time""github.com/quic-go/quic-go"
)const addr = "192.168.8.48:19940"func main() {tlsConf := &tls.Config{InsecureSkipVerify: true,NextProtos: []string{"HLD"},}conn, err := quic.DialAddr(context.Background(), addr, tlsConf, nil)if err != nil {log.Fatalf("Error dialing address: %v", err)}defer conn.CloseWithError(0, "")stream, err := conn.OpenStreamSync(context.Background())if err != nil {log.Fatalf("Error opening stream: %v", err)}defer stream.Close()// 发送数据var sendFlag int = 0go func() {for {sendFlag++sendBuffer := make([]byte, 1024)numberStr := "HLD_" + strconv.Itoa(sendFlag)copy(sendBuffer, numberStr)log.Printf("Send: %v", string(sendBuffer))err = sendData(stream, sendBuffer[:len(numberStr)])if err != nil {fmt.Errorf("Error writing to stream: %v", err)break}time.Sleep(1 * time.Second)}}()// 接收数据go func() {for {recvBuffer := make([]byte, 1024)recvBuffer, err = receiveData(stream, len(recvBuffer))if err != nil {fmt.Errorf("Error reading from stream: %v", err)break}log.Printf("Recv: %v", string(recvBuffer))}}()for {time.Sleep(10 * time.Second)}fmt.Println("Echo test successful")
}func sendData(stream quic.Stream, data []byte) error {_, err := stream.Write(data)if err != nil {return fmt.Errorf("error writing to stream: %w", err)}return nil
}func receiveData(stream quic.Stream, expectedLen int) ([]byte, error) {readBuf := make([]byte, expectedLen)//readPos := 0//for readPos < expectedLen {// n, err := stream.Read(readBuf[readPos:])// if err != nil {// return nil, fmt.Errorf("error reading from stream: %w", err)// }// readPos += n//}//return readBuf[:readPos], niln, err := stream.Read(readBuf)if err != nil {return nil, fmt.Errorf("error reading from stream: %w", err)}log.Printf("recvLen: %d\n", n)return readBuf[:n], nil//n, err := io.ReadFull(stream, readBuf)//if err != nil {// return nil, fmt.Errorf("error reading from stream: %w", err)//}//return readBuf[:n], nil
}
Quic服务端代码
package mainimport ("context""crypto/rand""crypto/rsa""crypto/tls""crypto/x509""encoding/pem""fmt""log""math/big""github.com/quic-go/quic-go"
)// go env -w GO111MODULE=on
// go get -u github.com/quic-go/quic-go
// go list -m github.com/quic-go/quic-goconst addr = "192.168.8.48:19940"func main() {quicConf := &quic.Config{InitialStreamReceiveWindow: 1 << 20, // 1 MBMaxStreamReceiveWindow: 6 << 20, // 6 MBInitialConnectionReceiveWindow: 2 << 20, // 2 MBMaxConnectionReceiveWindow: 12 << 20, // 12 MB}listener, err := quic.ListenAddr(addr, generateTLSConfig(), quicConf)if err != nil {log.Fatalf("Error listening on address: %v", err)}defer listener.Close()for {conn, err := listener.Accept(context.Background())if err != nil {log.Printf("Error accepting connection: %v", err)continue}go handleConnection(conn)fmt.Println("New client connected")}
}func handleConnection(conn quic.Connection) {for {// 接收数据流stream, err := conn.AcceptStream(context.Background())if err != nil {log.Printf("Error accepting stream: %v", err)return}remoteAddr := conn.RemoteAddr().String()fmt.Printf("Client connected from: %s\n", remoteAddr)go func() {defer stream.Close()for {data := make([]byte, 1024)nr, err := stream.Read(data)if err != nil {log.Printf("Error reading from stream: %v", err)return}log.Printf("Recv: %v\n", string(data))nw, err := stream.Write(data[:nr])if err != nil {log.Printf("Error writing to stream: %v", err)return}log.Printf("Send: %v, size: %d\n", string(data[:nr]), nw)}}()}
}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{"HLD"},}
}
Wireshark抓取Quic数据包
这篇关于Golang使用Quic-Go开源库实现Quic客户端和服务端的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!