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版本
  1. go version go1.22.6 linux/amd64
  2. github.com/quic-go/quic-go v0.46.0

Quic客户端代码

package main

import (
	"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 = 0
	go 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], nil

	n, 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 main

import (
	"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-go

const addr = "192.168.8.48:19940"

func main() {
	quicConf := &quic.Config{
		InitialStreamReceiveWindow:     1 << 20,  // 1 MB
		MaxStreamReceiveWindow:         6 << 20,  // 6 MB
		InitialConnectionReceiveWindow: 2 << 20,  // 2 MB
		MaxConnectionReceiveWindow:     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数据包

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓琴儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值