从某歌网站了解到,QUIC协议可能会成为未来的HTTP3协议的方向,这是一个使用UDP协议来达到TCP的效果,甚至某些方面更优,但是这家伙是以UDp为基础的,还是会存在UDP的一些不足,比如:安全问题,也就是反射攻击,即伪造原地址;还有就是这些年性能的优化提升都几乎针对 TCP ,使得 UDP 性能没有啥改进。当然随着 QUIC3 的发布,相信后续应该会有相对的投入。
想利用该协议来实现节点间数据通信,顾实验一番,下面有啥错误的地方,一定要指出来哦,感谢大家:
我是使用了这个go库来玩的:github.com/lucas-clemente/quic-go
服务端实现
const saddr = "localhost:9999"
func QUIC_Server_Run() {
listener, err := quic.ListenAddr(saddr, generateTLSConfig(), nil)
if err != nil {
fmt.Println(err)
}
bg := context.Background()
cancel, cancelFunc := context.WithCancel(bg)
defer cancelFunc()
for {
sess, err := listener.Accept(cancel)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(sess)
go dealSession(cancel, sess)
}
}
}
func dealSession(ctx context.Context, sess quic.Session) {
stream, err := sess.AcceptStream(ctx)
if err != nil {
panic(err)
} else {
result := bytes.NewBuffer(nil)
var buf [65542]byte // 由于 标识数据包长度 的只有两个字节 故数据包最大为 2^16+4(魔数)+2(长度标识)
for {
n, err := stream.Read(buf[0:])
result.Write(buf[0:n])
if err != nil {
if err == io.EOF {
panic(errors.New("客户端主动断开连接"))
} else {
//logger.PError(err, "read err:")
panic(err)
}
} else {
scanner := bufio.NewScanner(result)
scanner.Split(codec.PacketSplitFunc)
for scanner.Scan() {
fmt.Printf("Server: Got '%s'\n", string(scanner.Bytes()[6:]))
_, err = stream.Write(codec.Encode(scanner.Bytes()[6:]).Bytes())
if err != nil {
panic(err)
}
}
}
result.Reset()
}
}
}
// Setup a bare-bones TLS config for the server
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"}}
}
客户端实现
const addr = "localhost:9999"
const message = "ccccccccccccccccccccccd"
func QUIC_Client_run() {
session, err := quic.DialAddr(addr, &tls.Config{
InsecureSkipVerify: true, NextProtos: []string{
"quic"}}, nil)
if err != nil {
fmt.Println(err)
return
}
bg := context.Background()
cancel, cancelFunc := context.WithCancel(bg)
defer cancelFunc()
stream, err := session.OpenStreamSync(cancel)
if err != nil {
fmt.Println(err)
return
}
i := 0
go func() {
var data2 [65535]byte
result := bytes.NewBuffer(nil)
for {
n, err := stream.Read(data2[0:])
if err != nil {
panic(err)
}
result.Write(data2[0:n])
scanner := bufio.NewScanner(result)
scanner.Split(codec.PacketSplitFunc)
for scanner.Scan() {
fmt.Println(string(scanner.Bytes()[6:]))
}
}
}()
for {
bf := codec.Encode([]byte(message + strconv.Itoa(i)))
_, err = stream.Write(bf.Bytes())
if err != nil {
fmt.Println(err)
return
}
i++
}
}
两个处理粘包半包的函数
package codec
const (
// 包头魔术
PacketHead uint32 = 0x123456
)
func PacketSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 检查 atEOF 参数 和 数据包头部的四个字节是否 为 0x123456(我们定义的协议的魔数)
if !atEOF && len(data) > 6 && binary.BigEndian.Uint32(data[:4]) == PacketHead {
var l int16
// 读取 数据包 实际数据的长度(大小为0~2^16)
err = binary.Read(bytes.NewReader(data[4:6]), binary.BigEndian, &l)
if err != nil {
panic(err)
}
pl := int(l) + 6
if pl <= len(data) {
return pl, data[:pl], nil
}
}
return
}
func Encode(data []byte) *bytes.Buffer {
magicNum := make([]byte, 4)
binary.BigEndian.PutUint32(magicNum, 0x123456)
lenNum := make([]byte, 2)
binary.BigEndian.PutUint16(lenNum, uint16(len(data)))
packetBuf := bytes.NewBuffer(magicNum)
packetBuf.Write(lenNum)
packetBuf.Write(data)
return packetBuf
}
测试
不得不说,udp还是一如既往的块
看一下效果,我运行了客户端5秒钟,一下子就传输了5w条数据了
同样使用tcp协议来测试,发送5s:
基于UDP的QUIC传输数据效果确实不错;
QUIC 协议的主要目的,是为了整合 TCP 协议的可靠性和 UDP 协议的速度和效率。