Golang使用Quic-Go开源库实现Quic客户端和服务端

2024-08-30 03:04

本文主要是介绍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 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客户端和服务端的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1119614

相关文章

如何使用C#串口通讯实现数据的发送和接收

《如何使用C#串口通讯实现数据的发送和接收》本文详细介绍了如何使用C#实现基于串口通讯的数据发送和接收,通过SerialPort类,我们可以轻松实现串口通讯,并结合事件机制实现数据的传递和处理,感兴趣... 目录1. 概述2. 关键技术点2.1 SerialPort类2.2 异步接收数据2.3 数据解析2.

详解如何使用Python提取视频文件中的音频

《详解如何使用Python提取视频文件中的音频》在多媒体处理中,有时我们需要从视频文件中提取音频,本文为大家整理了几种使用Python编程语言提取视频文件中的音频的方法,大家可以根据需要进行选择... 目录引言代码部分方法扩展引言在多媒体处理中,有时我们需要从视频文件中提取音频,以便进一步处理或分析。本文

mybatis-plus 实现查询表名动态修改的示例代码

《mybatis-plus实现查询表名动态修改的示例代码》通过MyBatis-Plus实现表名的动态替换,根据配置或入参选择不同的表,本文主要介绍了mybatis-plus实现查询表名动态修改的示... 目录实现数据库初始化依赖包配置读取类设置 myBATis-plus 插件测试通过 mybatis-plu

使用Dify访问mysql数据库详细代码示例

《使用Dify访问mysql数据库详细代码示例》:本文主要介绍使用Dify访问mysql数据库的相关资料,并详细讲解了如何在本地搭建数据库访问服务,使用ngrok暴露到公网,并创建知识库、数据库访... 1、在本地搭建数据库访问的服务,并使用ngrok暴露到公网。#sql_tools.pyfrom

Qt把文件夹从A移动到B的实现示例

《Qt把文件夹从A移动到B的实现示例》本文主要介绍了Qt把文件夹从A移动到B的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录如何移动一个文件? 如何移动文件夹(包含里面的全部内容):如何删除文件夹:QT 文件复制,移动(

Flask 验证码自动生成的实现示例

《Flask验证码自动生成的实现示例》本文主要介绍了Flask验证码自动生成的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 目录生成图片以及结果处理验证码蓝图html页面展示想必验证码大家都有所了解,但是可以自己定义图片验证码

VSCode配置Anaconda Python环境的实现

《VSCode配置AnacondaPython环境的实现》VisualStudioCode中可以使用Anaconda环境进行Python开发,本文主要介绍了VSCode配置AnacondaPytho... 目录前言一、安装 Visual Studio Code 和 Anaconda二、创建或激活 conda

使用mvn deploy命令上传jar包的实现

《使用mvndeploy命令上传jar包的实现》本文介绍了使用mvndeploy:deploy-file命令将本地仓库中的JAR包重新发布到Maven私服,文中通过示例代码介绍的非常详细,对大家的学... 目录一、背景二、环境三、配置nexus上传账号四、执行deploy命令上传包1. 首先需要把本地仓中要

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

MySQL中实现多表查询的操作方法(配sql+实操图+案例巩固 通俗易懂版)

《MySQL中实现多表查询的操作方法(配sql+实操图+案例巩固通俗易懂版)》本文主要讲解了MySQL中的多表查询,包括子查询、笛卡尔积、自连接、多表查询的实现方法以及多列子查询等,通过实际例子和操... 目录复合查询1. 回顾查询基本操作group by 分组having1. 显示部门号为10的部门名,员