使用go语言在本地搭建quic服务端与客户端
操作系统:Ubuntu 22.04
环境配置和依赖安装
#安装go语言(需要1.18+版本)
wget https://go.dev/dl/go1.21.4.linux-amd64.tar.gz
#删除旧版本go并解压新版本到系统目录
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.21.4.linux-amd64.tar.gz
#永久配置go语言的环境变量
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
#安装依赖库
sudo apt update
sudo apt install -y libssl-dev git make gcc wireshark-qt
#若wireshark-qt不存在,改为wireshark也可以
安装quic库
#创建项目目录
mkdir quic-demo && cd quic-demo
go mod init quic-demo
#安装quic库
go get github.com/quic-go/quic-go
#生成TLS证书(QUIC强制要求加密)
openssl req -x509 -newkey rsa:2048 -nodes -keyout server.key -out server.crt -days 365 -subj '/CN=localhost'
生成服务端代码
#创建server.go并进行编辑,我使用如下方法创建
touch server.go
nano server.go
//服务端代码(server.go)
package main
import (
"context"
"crypto/rsa"//有可能不需要,注释掉即可
"crypto/tls"
"crypto/x509"//有可能不需要,注释掉即可
"fmt"
"io"
"log"
"os"//有可能不需要,注释掉即可
"github.com/quic-go/quic-go"
)
func main() {
// 加载TLS证书
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
// 配置QUIC监听器
listener, err := quic.ListenAddr(
"0.0.0.0:4242",
&tls.Config{
Certificates: []tls.Certificate{cert},
NextProtos: []string{"quic-demo"},
},
nil, // 使用默认QUIC配置
)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
fmt.Println("QUIC Server listening on :4242")
// 接受客户端连接
conn, err := listener.Accept(context.Background())
if err != nil {
log.Fatal(err)
}
// 接受数据流
stream, err := conn.AcceptStream(context.Background())
if err != nil {
log.Fatal(err)
}
// 读取客户端消息
buf := make([]byte, 1024)
n, err := stream.Read(buf)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Printf("Received: %s\n", buf[:n])
// 发送响应
_, err = stream.Write([]byte("Hello from QUIC server!"))
if err != nil {
log.Fatal(err)
}
}
生成客户端代码
#创建client.go并进行编辑,我使用如下方法创建
touch client.go
nano client.go
//客户端代码(cilent.go)
package main
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"os"
"github.com/quic-go/quic-go"
}
func main() {
// 加载自签名证书
pool := x509.NewCertPool()
certData, err := os.ReadFile("server.crt")
if err != nil {
log.Fatal(err)
}
pool.AppendCertsFromPEM(certData)
// 配置QUIC连接
tlsConf := &tls.Config{
RootCAs: pool,
InsecureSkipVerify: true, // 跳过证书验证(仅用于测试)
NextProtos: []string{"quic-demo"},
}
// 连接到服务器
conn, err := quic.DialAddr(
context.Background(),
"localhost:4242",
tlsConf,
nil, // 使用默认QUIC配置
)
if err != nil {
log.Fatal(err)
}
defer conn.CloseWithError(0, "")
// 创建数据流
stream, err := conn.OpenStreamSync(context.Background())
if err != nil {
log.Fatal(err)
}
defer stream.Close()
// 发送消息
_, err = stream.Write([]byte("Hello from QUIC client!"))
if err != nil {
log.Fatal(err)
}
// 读取响应
buf := make([]byte, 1024)
n, err := stream.Read(buf)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Printf("Received: %s\n", buf[:n])
}
编译和运行
#编译时需要添加quic标签
go build -tags quic server.go
go build -tags quic client.go
#设置SSL密钥日志文件路径(必须步骤)
export SSLKEYLOGFILE=~/sslkeylog.log
测试
wireshark设置
- 在Preferences -> Protocols -> TLS设置(Pre)-Master-Secret log文件名,需要输入sslkeylog.log的完整路径
- 开始捕获(选择正确的网卡,我是在是本地回环地址lo)
启动服务端
# 第一个终端启动服务端(quic-demo文件下)
sudo ./server
启动客户端
# 第二个终端启动客户端(quic-demo文件下)
#需要保持SSLKEYLOGFILE环境变量
SSLKEYLOGFILE=~/sslkeylog.log ./client
SSLKEYLOGFILE=~/sslkeylog.log: 这是一个环境变量设置,指示程序在运行期间将 SSL/TLS 密钥日志输出到指定文件 ~/sslkeylog.log。这个环境变量通常用于调试和分析 SSL/TLS 流量。通过这种方式,可以保存密钥以便后续使用工具(如 Wireshark)进行抓包分析。
./client: 这是一个可执行文件的运行命令,表示在当前目录下执行名为 client 的程序。
启动客户端后可能的报错
-
failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details.该消息表示接收缓冲区的大小不能满足程序的需求。具体来说,当前缓冲区大小为 208 KiB,而程序想要将其增加到 7168 KiB,但是实际增加后只得到了 416 KiB。
-
原因:错误的原因主要是因为系统的 UDP 接收缓冲区的配置不足,不能满足程序的需求。QUIC 是基于 UDP 协议的,因此对于 UDP 连接的缓冲区设置会影响 QUIC 的性能和稳定性。
-
解决方法:为了调整 UDP 缓冲区大小,可以按照以下步骤进行操作
#将 UDP 接收和发送缓冲区的最大值设置为 8 MiB(8388608 字节) sudo sysctl -w net.core.rmem_max=8388608 sudo sysctl -w net.core.wmem_max=8388608