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

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

  1package main
  2
  3import (
  4	"context"
  5	"crypto/rand"
  6	"crypto/rsa"
  7	"crypto/tls"
  8	"crypto/x509"
  9	"encoding/pem"
 10	"fmt"
 11	"io"
 12	"log"
 13	"math/big"
 14
 15	"github.com/quic-go/quic-go"
 16)
 17
 18const addr = "localhost:4242"
 19
 20// the message we sent to server you can also change it!
 21const message = "hello gifarsi!"
 22
 23// starting the server and call the client function.
 24func main() {
 25	go func() { log.Fatal(echoServer()) }()
 26
 27	err := clientMain()
 28	if err != nil {
 29		panic(err)
 30	}
 31}
 32
 33// this function start our echo server
 34func echoServer() error {
 35  // make a new listner with quic
 36	listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
 37	if err != nil {
 38		return err
 39	}
 40
 41  // accept incoming connections
 42	conn, err := listener.Accept(context.Background())
 43	if err != nil {
 44		return err
 45	}
 46
 47  // accept incoming streams
 48	stream, err := conn.AcceptStream(context.Background())
 49	if err != nil {
 50		panic(err)
 51	}
 52 
 53	// Echo using the loggingWriter
 54	_, err = io.Copy(loggingWriter{stream}, stream)
 55	return err
 56}
 57
 58// client function thah send the message to our server
 59func clientMain() error {
 60  // set up a tls config
 61	tlsConf := &tls.Config{
 62		InsecureSkipVerify: true,
 63		NextProtos:         []string{"quic-echo-example"},
 64	}
 65  // dial with our udp server
 66	conn, err := quic.DialAddr(context.Background(), addr, tlsConf, nil)
 67	if err != nil {
 68		return err
 69	}
 70
 71  // opening a new stream from our connection
 72	stream, err := conn.OpenStreamSync(context.Background())
 73	if err != nil {
 74		return err
 75	}
 76
 77  // write the message over the stream
 78	fmt.Printf("Client: Sending '%s'\n", message)
 79	_, err = stream.Write([]byte(message))
 80	if err != nil {
 81		return err
 82	}
 83
 84  // read and print incoming answer from server
 85	buf := make([]byte, len(message))
 86	_, err = io.ReadFull(stream, buf)
 87	if err != nil {
 88		return err
 89	}
 90	fmt.Printf("Client: Got '%s'\n", buf)
 91
 92	return nil
 93}
 94
 95// A wrapper for io.Writer that also logs the message.
 96type loggingWriter struct{ io.Writer }
 97
 98func (w loggingWriter) Write(b []byte) (int, error) {
 99	fmt.Printf("Server: Got '%s'\n", string(b))
100	return w.Writer.Write(b)
101}
102
103// Setup a bare-bones TLS config for the server
104func generateTLSConfig() *tls.Config {
105	key, err := rsa.GenerateKey(rand.Reader, 1024)
106	if err != nil {
107		panic(err)
108	}
109	template := x509.Certificate{SerialNumber: big.NewInt(1)}
110	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
111	if err != nil {
112		panic(err)
113	}
114	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
115	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
116
117	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
118	if err != nil {
119		panic(err)
120	}
121	return &tls.Config{
122		Certificates: []tls.Certificate{tlsCert},
123		NextProtos:   []string{"quic-echo-example"},
124	}
125}

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

go run main.go

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

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

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