پروتکل 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 توسعه دهید.