5.10 پروتکل quic

5.10 پروتکل quic

پروتکل quic یک پروتکل توسعه یافته توسط google است. این پروتکل برای امنیت و سرعت بیشتر توسعه داده شده است.

پروتکل quic از UDP استفاده میکند و در لایه transport قرار میگیرد.

این پروتکل اجازه ایجاد چند کانکشن بصورت همزمان را فراهم میکنید و همچنین http/3 بر اساس این پروتکل طراحی و ایجاد شده است.

چند مورد از ویژگی های کلیدی quic : ۱. ایجاد کانکشن سریع تر به دلیل اسفاده از udp. ۲. نیازی به دست دادن سه مرحله مثل tcp ندارد. ۳. بصورت پیش فرص از رمزنگاری استفاده میکند.

در ادامه با استفاده از یک پکیج خارجی به نام quic-go یک سرور ساده با این پروتکل ایجاد میکنیم و یک پیام به آن ارسال میکنیم (عملکرد سرور ما تنها برگرداندن همان متن یا به اصطلاحی echo کردن آن است)

برای شروع کار ابتدا نیاز داریم پکیج مورد نظر خود را نصب کنیم

با استفاده از دستور زیر: go get github.com/quic-go/quic-go

بعد از اتمام مرحله نصب شروع به نوشتن کد سرور و کلاینت خود میکنیم.

package main

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"
	"log"
	"math/big"

	"github.com/quic-go/quic-go"
)

const addr = "localhost:4242"

// the message we sent to server you can also change it!
const message = "hello gifarsi!"

// starting the server and call the client function.
func main() {
	go func() { log.Fatal(echoServer()) }()

	err := clientMain()
	if err != nil {
		panic(err)
	}
}

// this function start our echo server
func echoServer() error {
  // make a new listner with quic
	listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
	if err != nil {
		return err
	}

  // accept incoming connections
	conn, err := listener.Accept(context.Background())
	if err != nil {
		return err
	}

  // accept incoming streams
	stream, err := conn.AcceptStream(context.Background())
	if err != nil {
		panic(err)
	}

	// Echo using the loggingWriter
	_, err = io.Copy(loggingWriter{stream}, stream)
	return err
}

// client function thah send the message to our server
func clientMain() error {
  // set up a tls config
	tlsConf := &tls.Config{
		InsecureSkipVerify: true,
		NextProtos:         []string{"quic-echo-example"},
	}
  // dial with our udp server
	conn, err := quic.DialAddr(context.Background(), addr, tlsConf, nil)
	if err != nil {
		return err
	}

  // opening a new stream from our connection
	stream, err := conn.OpenStreamSync(context.Background())
	if err != nil {
		return err
	}

  // write the message over the stream
	fmt.Printf("Client: Sending '%s'\n", message)
	_, err = stream.Write([]byte(message))
	if err != nil {
		return err
	}

  // read and print incoming answer from server
	buf := make([]byte, len(message))
	_, err = io.ReadFull(stream, buf)
	if err != nil {
		return err
	}
	fmt.Printf("Client: Got '%s'\n", buf)

	return nil
}

// A wrapper for io.Writer that also logs the message.
type loggingWriter struct{ io.Writer }

func (w loggingWriter) Write(b []byte) (int, error) {
	fmt.Printf("Server: Got '%s'\n", string(b))
	return w.Writer.Write(b)
}

// 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-echo-example"},
	}
}

بعد از اتمام نوشتن کد های سرور میتوانیم کد خود را اجرا و تست کنیم

go run main.go

خروجی لاگ های ما به این صورت خواهد بود :

Client: Sending 'hello gifarsi!'
Server: Got 'hello gifarsi!'
Client: Got 'hello gifarsi!'

در انتها توجه داشته باشید این یک مثال ساده از quic در گولنگ بود شما میتوانید با استفاده از همین پکیج سرور های کامل تر و پیچیده تر را توسعه دهید و همچنین با استفاده از quic-go/http3 یک سرور http3 توسعه دهید.