buffio flush

88 阅读1分钟

It's inefficient if we write to a file one character or word at a time. The write to file works best when done in blocks or chunks.

// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err error) {
   for len(p) > b.Available() && b.err == nil {
      var n int
      if b.Buffered() == 0 {
         // Large write, empty buffer.
         // Write directly from p to avoid copy.
         n, b.err = b.wr.Write(p)
      } else {
         n = copy(b.buf[b.n:], p)
         b.n += n
         b.Flush()
      }
      nn += n
      p = p[n:]
   }
   if b.err != nil {
      return nn, b.err
   }
   n := copy(b.buf[b.n:], p)
   b.n += n
   nn += n
   return nn, nil
}

b.wr 是在 newBufioWriterSize 的时候传进去的

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...

c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
...
}

gin分片传输实现代码

package main

import (
    "fmt"
    "net/http"
    "time"

    "github.com/gin-gonic/gin"
)

func main() {

r := gin.Default()
r.GET("/test_stream", func(c *gin.Context) {
   w := c.Writer
   header := w.Header()
   header.Set("Content-Type", "text/html")
   w.WriteHeader(http.StatusOK)
   w.Write([]byte(`
           <html>
                   <body>
       `))
   w.Flush()

   // 这里对每次循环输出都进行Flush刷新输出
   for i := 0; i < 10; i++ {
      w.Write([]byte(fmt.Sprintf(`
               <h3>%d</h3>
           `, i)))
      //w.Flush()
      w.Flush()
      time.Sleep(time.Duration(1) * time.Second)
   }

   w.Write([]byte(`

                   </body>
           </html>
       `))
   w.Flush()
})

r.Run("127.0.0.1:8080")

}

分块传输的基础:http的 transfer-encoding:chunked 协议

分块传输的编码规则如下: 1)每个分块包含两个部分,<长度头>和&<数据块>

当输出内容太大时,就可以使用分块传输的方式。分块传输是基于http的Transfer-Encoding: chunked协议进行的

image.png

image.png