我们可以通过QUIC/HTTP3将请求从服务器发送到客户端,并获取响应。

huangapple go评论107阅读模式
英文:

Can we send request form server to client and get response through quic/http3?

问题

我正在使用quic-go来实现我的想法,我需要服务器主动向客户端发送请求并获取响应,就像客户端通常发送请求到Web服务器一样。但是使用quic-go,在建立连接之后,服务器可以初始化流以向客户端发送请求并获取响应吗?我尝试过,但还没有成功。下面的代码是来自示例目录中的echo.go,两个注释行之间的部分是我添加的。

  1. package main
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "encoding/pem"
  9. "fmt"
  10. "io"
  11. "log"
  12. "math/big"
  13. "github.com/lucas-clemente/quic-go"
  14. )
  15. const addr = "localhost:4242"
  16. const message = "foobar"
  17. // 我们启动一个服务器,在客户端打开的第一个流上回显数据,
  18. // 然后与客户端建立连接,发送消息,并等待接收。
  19. func main() {
  20. go func() { log.Fatal(echoServer()) }()
  21. err := clientMain()
  22. if err != nil {
  23. panic(err)
  24. }
  25. }
  26. // 启动一个服务器,在客户端打开的第一个流上回显所有数据
  27. func echoServer() error {
  28. listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
  29. if err != nil {
  30. return err
  31. }
  32. conn, err := listener.Accept(context.Background())
  33. if err != nil {
  34. return err
  35. }
  36. stream, err := conn.AcceptStream(context.Background())
  37. if err != nil {
  38. panic(err)
  39. }
  40. // 通过loggingWriter回显
  41. _, err = io.Copy(loggingWriter{stream}, stream)
  42. if err != nil {
  43. panic(err)
  44. }
  45. //------------------------------
  46. stream1, err := conn.OpenStream()
  47. if err != nil {
  48. panic(err)
  49. }
  50. message := "aaaaa"
  51. fmt.Printf("2-Server: Sending '%s'\n", message)
  52. _, err = stream1.Write([]byte(message))
  53. //------------------------------
  54. return err
  55. }
  56. func clientMain() error {
  57. tlsConf := &tls.Config{
  58. InsecureSkipVerify: true,
  59. NextProtos: []string{"quic-echo-example"},
  60. }
  61. conn, err := quic.DialAddr(addr, tlsConf, nil)
  62. if err != nil {
  63. return err
  64. }
  65. stream, err := conn.OpenStreamSync(context.Background())
  66. if err != nil {
  67. return err
  68. }
  69. fmt.Printf("Client: Sending '%s'\n", message)
  70. _, err = stream.Write([]byte(message))
  71. if err != nil {
  72. return err
  73. }
  74. buf := make([]byte, len(message))
  75. _, err = io.ReadFull(stream, buf)
  76. if err != nil {
  77. return err
  78. }
  79. fmt.Printf("Client: Got '%s'\n", buf)
  80. err = stream.Close()
  81. if err != nil {
  82. return err
  83. }
  84. //-------------------------------
  85. for {
  86. stream1, err := conn.AcceptStream(context.Background())
  87. if err != nil {
  88. panic(err)
  89. }
  90. buf1 := make([]byte, len(message))
  91. _, err = io.ReadFull(stream1, buf1)
  92. if err != nil {
  93. panic(err)
  94. }
  95. fmt.Printf("2-Client: Got '%s'\n", buf1)
  96. err = stream1.Close()
  97. if err != nil {
  98. panic(err)
  99. }
  100. }
  101. //-------------------------------
  102. return nil
  103. }
  104. // 一个同时记录消息的io.Writer包装器。
  105. type loggingWriter struct{ io.Writer }
  106. func (w loggingWriter) Write(b []byte) (int, error) {
  107. fmt.Printf("Server: Got '%s'\n", string(b))
  108. return w.Writer.Write(b)
  109. }
  110. // 为服务器设置一个简单的TLS配置
  111. func generateTLSConfig() *tls.Config {
  112. key, err := rsa.GenerateKey(rand.Reader, 1024)
  113. if err != nil {
  114. panic(err)
  115. }
  116. template := x509.Certificate{SerialNumber: big.NewInt(1)}
  117. certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
  118. if err != nil {
  119. panic(err)
  120. }
  121. keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  122. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
  123. tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
  124. if err != nil {
  125. panic(err)
  126. }
  127. return &tls.Config{
  128. Certificates: []tls.Certificate{tlsCert},
  129. NextProtos: []string{"quic-echo-example"},
  130. }
  131. }
英文:

I'm using quic-go to implement my thought, I need the server to forwardly send request to client to get response, just like we do that client sends request to web server commonly. But with quic-go, after connection is setup, can server initialize streams to send request to client and get responses? I did a trying but haven't made it work. The code below is from the echo.go of example dir, the two parts between comment lines are added by me.

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

答案1

得分: 2

你离答案很近。

注意服务器在写入并返回数据之后,会在客户端有机会读取和处理数据之前调用log.Fatal

请注意message变量,一个长度为6,另一个长度只有5。

请正确关闭流,服务器在交接之前没有结束它。

  1. package main
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "encoding/pem"
  9. "fmt"
  10. "io"
  11. "log"
  12. "math/big"
  13. "github.com/lucas-clemente/quic-go"
  14. )
  15. const addr = "localhost:4242"
  16. const message = "foobar"
  17. // 我们启动一个服务器,在客户端打开的第一个流上回显数据,
  18. // 然后与客户端建立连接,发送消息,并等待接收。
  19. func main() {
  20. go func() {
  21. err := echoServer()
  22. if err != nil {
  23. log.Println(err)
  24. }
  25. }()
  26. err := clientMain()
  27. if err != nil {
  28. panic(err)
  29. }
  30. }
  31. // 启动一个服务器,在客户端打开的第一个流上回显所有数据
  32. func echoServer() error {
  33. listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
  34. if err != nil {
  35. return err
  36. }
  37. conn, err := listener.Accept(context.Background())
  38. if err != nil {
  39. return err
  40. }
  41. stream, err := conn.AcceptStream(context.Background())
  42. if err != nil {
  43. panic(err)
  44. }
  45. // 通过loggingWriter回显
  46. _, err = io.Copy(loggingWriter{stream}, stream)
  47. if err != nil {
  48. log.Println(err)
  49. return err
  50. }
  51. //------------------------------
  52. fmt.Printf("Server: 打开流\n")
  53. stream1, err := conn.OpenStream()
  54. if err != nil {
  55. return err
  56. }
  57. fmt.Printf("2-Server: 发送 '%s'\n", message)
  58. var n int
  59. n, err = stream1.Write([]byte(message))
  60. fmt.Printf("Server: 写入 %v %v\n", n, err)
  61. stream1.Close()
  62. //------------------------------
  63. return err
  64. }
  65. func clientMain() error {
  66. tlsConf := &tls.Config{
  67. InsecureSkipVerify: true,
  68. NextProtos: []string{"quic-echo-example"},
  69. }
  70. conn, err := quic.DialAddr(addr, tlsConf, nil)
  71. if err != nil {
  72. return err
  73. }
  74. stream, err := conn.OpenStreamSync(context.Background())
  75. if err != nil {
  76. return err
  77. }
  78. fmt.Printf("Client: 发送 '%s'\n", message)
  79. _, err = stream.Write([]byte(message))
  80. if err != nil {
  81. return err
  82. }
  83. buf := make([]byte, len(message))
  84. _, err = io.ReadFull(stream, buf)
  85. if err != nil {
  86. return err
  87. }
  88. fmt.Printf("Client: 收到 '%s'\n", buf)
  89. err = stream.Close()
  90. if err != nil {
  91. return err
  92. }
  93. //-------------------------------
  94. for {
  95. fmt.Printf("Client: 接受流\n")
  96. stream1, err := conn.AcceptStream(context.Background())
  97. if err != nil {
  98. return err
  99. }
  100. fmt.Printf("Client: 收到流 %v\n", err)
  101. buf1 := make([]byte, len(message))
  102. _, err = io.ReadFull(stream1, buf1)
  103. if err != nil {
  104. return err
  105. }
  106. fmt.Printf("2-Client: 收到 '%s'\n", buf1)
  107. err = stream1.Close()
  108. if err != nil {
  109. return err
  110. }
  111. break
  112. }
  113. //-------------------------------
  114. return nil
  115. }
  116. // 一个同时记录消息的io.Writer包装器。
  117. type loggingWriter struct{ io.Writer }
  118. func (w loggingWriter) Write(b []byte) (int, error) {
  119. fmt.Printf("Server: 收到 '%s'\n", string(b))
  120. return w.Writer.Write(b)
  121. }
  122. // 为服务器设置一个基本的TLS配置
  123. func generateTLSConfig() *tls.Config {
  124. key, err := rsa.GenerateKey(rand.Reader, 1024)
  125. if err != nil {
  126. panic(err)
  127. }
  128. template := x509.Certificate{SerialNumber: big.NewInt(1)}
  129. certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
  130. if err != nil {
  131. panic(err)
  132. }
  133. keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  134. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
  135. tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
  136. if err != nil {
  137. panic(err)
  138. }
  139. return &tls.Config{
  140. Certificates: []tls.Certificate{tlsCert},
  141. NextProtos: []string{"quic-echo-example"},
  142. }
  143. }
英文:

You are close.

Notice how the server writes, then returns, it reaches out to log.Fatal before the client had a chance to read and handle the data.

Take care to the message variable, one is 6 length long, the other one is only 5 length long.

Properly close your stream, the server was not ending it ending before handing over;

  1. package main
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "crypto/rsa"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "encoding/pem"
  9. "fmt"
  10. "io"
  11. "log"
  12. "math/big"
  13. "github.com/lucas-clemente/quic-go"
  14. )
  15. const addr = "localhost:4242"
  16. const message = "foobar"
  17. // We start a server echoing data on the first stream the client opens,
  18. // then connect with a client, send the message, and wait for its receipt.
  19. func main() {
  20. go func() {
  21. err := echoServer()
  22. if err != nil {
  23. log.Println(err)
  24. }
  25. }()
  26. err := clientMain()
  27. if err != nil {
  28. panic(err)
  29. }
  30. }
  31. // Start a server that echos all data on the first stream opened by the client
  32. func echoServer() error {
  33. listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
  34. if err != nil {
  35. return err
  36. }
  37. conn, err := listener.Accept(context.Background())
  38. if err != nil {
  39. return err
  40. }
  41. stream, err := conn.AcceptStream(context.Background())
  42. if err != nil {
  43. panic(err)
  44. }
  45. // Echo through the loggingWriter
  46. _, err = io.Copy(loggingWriter{stream}, stream)
  47. if err != nil {
  48. log.Println(err)
  49. return err
  50. }
  51. //------------------------------
  52. fmt.Printf("Server: open stream\n")
  53. stream1, err := conn.OpenStream()
  54. if err != nil {
  55. return err
  56. }
  57. fmt.Printf("2-Server: Sending '%s'\n", message)
  58. var n int
  59. n, err = stream1.Write([]byte(message))
  60. fmt.Printf("Server: write %v %v\n", n, err)
  61. stream1.Close()
  62. //------------------------------
  63. return err
  64. }
  65. func clientMain() error {
  66. tlsConf := &tls.Config{
  67. InsecureSkipVerify: true,
  68. NextProtos: []string{"quic-echo-example"},
  69. }
  70. conn, err := quic.DialAddr(addr, tlsConf, nil)
  71. if err != nil {
  72. return err
  73. }
  74. stream, err := conn.OpenStreamSync(context.Background())
  75. if err != nil {
  76. return err
  77. }
  78. fmt.Printf("Client: Sending '%s'\n", message)
  79. _, err = stream.Write([]byte(message))
  80. if err != nil {
  81. return err
  82. }
  83. buf := make([]byte, len(message))
  84. _, err = io.ReadFull(stream, buf)
  85. if err != nil {
  86. return err
  87. }
  88. fmt.Printf("Client: Got '%s'\n", buf)
  89. err = stream.Close()
  90. if err != nil {
  91. return err
  92. }
  93. //-------------------------------
  94. for {
  95. fmt.Printf("Client: accept stream\n")
  96. stream1, err := conn.AcceptStream(context.Background())
  97. if err != nil {
  98. return err
  99. }
  100. fmt.Printf("Client: got stream %v\n", err)
  101. buf1 := make([]byte, len(message))
  102. _, err = io.ReadFull(stream1, buf1)
  103. if err != nil {
  104. return err
  105. }
  106. fmt.Printf("2-Client: Got '%s'\n", buf1)
  107. err = stream1.Close()
  108. if err != nil {
  109. return err
  110. }
  111. break
  112. }
  113. //-------------------------------
  114. return nil
  115. }
  116. // A wrapper for io.Writer that also logs the message.
  117. type loggingWriter struct{ io.Writer }
  118. func (w loggingWriter) Write(b []byte) (int, error) {
  119. fmt.Printf("Server: Got '%s'\n", string(b))
  120. return w.Writer.Write(b)
  121. }
  122. // Setup a bare-bones TLS config for the server
  123. func generateTLSConfig() *tls.Config {
  124. key, err := rsa.GenerateKey(rand.Reader, 1024)
  125. if err != nil {
  126. panic(err)
  127. }
  128. template := x509.Certificate{SerialNumber: big.NewInt(1)}
  129. certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
  130. if err != nil {
  131. panic(err)
  132. }
  133. keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  134. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
  135. tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
  136. if err != nil {
  137. panic(err)
  138. }
  139. return &tls.Config{
  140. Certificates: []tls.Certificate{tlsCert},
  141. NextProtos: []string{"quic-echo-example"},
  142. }
  143. }

huangapple
  • 本文由 发表于 2022年5月26日 20:24:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/72391766.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定