360雲計算 360雲計算 併發
女主宣言app
Go語言以其自己具備的高併發特性,在雲計算開發中,獲得了普遍的應用,也深受廣大開發者的歡迎。可是你們對go語言真的理解了麼?本文做者通過對go語言的多年實踐應用,現對go語言中如何從io.Reader中讀數據進行了詳細介紹,相信對於go語言愛好者有很大的幫助。下來就跟隨做者一塊兒學習下吧。tcp
PS:豐富的一線技術、多元化的表現形式,盡在「360雲計算」,點關注哦!ide
1高併發
概述學習
開發過程當中,咱們常常從io.Reader中讀取數據。type Reader interface {ui
Read(p []byte) (n int, err error)雲計算
}url
package mainspa
import (
"fmt"
"io"
"net"
)
func main() {
// 創建tcp鏈接
conn, err := net.Dial("tcp", "www.findme.wang:80")
if err != nil {
fmt.Println("dial error:", err)
return
}
defer conn.Close() // 關閉鏈接
// 構建http協議內容,發起http請求
httpReq := `GET / HTTP/1.0
Host: www.findme.wang
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Content-Type:application/x-www-form-urlencoded
Content-Length:0
`
_, err = fmt.Fprintf(conn, httpReq)
if err != nil {
fmt.Println("http request error:", err)
return
}
// read from conn
rsData := make([]byte, 0)
for {
// 每次最多讀取512 個字節
var tmp = make([]byte, 512)
n, err := conn.Read(tmp)
if n >= 0 {
rsData = append(rsData, tmp[:n]...)
}
if err == io.EOF {
fmt.Println("數據讀取完畢")
break
} else if err != nil {
fmt.Println("讀取數據報錯:", err)
break
}
}
fmt.Println("讀取的數據長度:", len(rsData))
}
在案例中,咱們利用for循環反覆的讀,有沒有簡潔的方式呢?2
利用io.copy讀取
io.copy定義以下:func Copy(dst Writer, src Reader) (written int64, err error) {
return copyBuffer(dst, src, nil)
}
將reader中內容讀取到dst中的數據,讀取到dst中,因此咱們須要一個writer 就行,來吧,封裝一個以下:
package main
import (
"fmt"
"io"
"net"
)
type MyWriter struct {
data []byte
}
func (m *MyWriter) Write(p []byte) (n int, err error) {
if m.data == nil {
m.data = make([]byte, 0)
}
if p != nil && len(p) != 0 {
m.data = append(m.data, p...)
}
return len(p), nil
}
func main() {
// 創建tcp鏈接
conn, err := net.Dial("tcp", "www.findme.wang:80")
if err != nil {
fmt.Println("dial error:", err)
return
}
defer conn.Close() // 關閉鏈接
// 構建http協議內容,發起http請求
httpReq := `GET / HTTP/1.0
Host: www.findme.wang
User- Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Content-Type:application/x-www-form-urlencoded
Content-Length:0
`
_, err = fmt.Fprintf(conn, httpReq)
if err != nil {
fmt.Println("http request error:", err)
return
}
w := new(MyWriter)
n, err := io.Copy(w, conn) // 將 conn中的數據讀取到 writer中
if err != nil {
fmt.Println("讀取err ", err)
}
//fmt.Println(string(w.data))// 打印數據
fmt.Println("讀取的數據長度:", n)
}
從io讀取數據雖然是簡單了,可是須要封裝一個writer。那麼,go裏面是否有相似的writer呢?可以讓咱們很容易獲取數據的writer呢?
因而,咱們找到了strings.buffer ,以下:// A Builder is used to efficiently build a string using Write methods.
// It minimizes memory copying. The zero value is ready to use.
// Do not copy a non-zero Builder.
type Builder struct {
addr *Builder // of receiver, to detect copies by value
buf []byte
}
有了strings.buffer,代碼又可精簡一波。
package main
import (
"fmt"
"io"
"net"
"strings"
)
func main() {
// 創建tcp鏈接
conn, err := net.Dial("tcp", "www.findme.wang:80")
if err != nil {
fmt.Println("dial error:", err)
return
}
defer conn.Close() // 關閉鏈接
// 構建http協議內容,發起http請求
httpReq := `GET / HTTP/1.0
Host: www.findme.wang
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Content-Type:application/x-www-form-urlencoded
Content-Length:0
`
_, err = fmt.Fprintf(conn, httpReq)
if err != nil {
fmt.Println("http request error:", err)
return
}
var sb strings.Builder
n, err := io.Copy(&sb, conn) // 將 conn中的數據讀取到 writer中
if err != nil {
fmt.Println("讀取err ", err)
}
fmt.Println(sb.String()) // print res
fmt.Println("讀取的數據長度:", n)
}
除了,使用strings.buffer,咱們還能夠使用bytes.Buffer。3
使用ioutil.ReadAll
ReadAll(r io.Reader) ([]byte, error) 是一次性從輸入流(reader)中讀取全量數據,直到發送錯誤或EOF。若讀取失敗,返回已讀數據和err;若讀取成功,則返回全量數據和nil。即改方法,不會返回EOF,案例以下:package main
import (
"fmt"
"io/ioutil"
"net"
)
func main() {
// 創建tcp鏈接
conn, err := net.Dial("tcp", "www.findme.wang:80")
if err != nil {
fmt.Println("dial error:", err)
return
}
defer conn.Close() // 關閉鏈接
// 構建http協議內容,發起http請求
httpReq := `GET / HTTP/1.0
Host: www.findme.wang
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Content-Type:application/x-www-form-urlencoded
Content-Length:0
`
_, err = fmt.Fprintf(conn, httpReq)
if err != nil {
fmt.Println("http request error:", err)
return
}
data, err := ioutil.ReadAll(conn)
if err != nil {
fmt.Println("讀取err ", err)
}
fmt.Println(string(data)) // print res
fmt.Println("讀取的數據長度:", len(data))
}
4
補充
此外,咱們還能夠使用io包提供的一些方法,好比:io.ReadAtLeast、io.ReadFull等.一、io.ReadAtLeast從輸入流中至少min個字節,放到buf中,返回讀取的字節數和err,結構以下:func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
if len(buf) < min {
return 0, ErrShortBuffer
}
for n < min && err == nil {
var nn int
nn, err = r.Read(buf[n:])
n += nn
}
if n >= min { //讀取字節不小於min的時候,把err 設置nil
err = nil
} else if n > 0 && err == EOF {
err = ErrUnexpectedEOF
}
return
}
若是buf的長度小於 min,會觸發ErrShortBuffer 。
func ReadFull(r Reader, buf []byte) (n int, err error) {
return ReadAtLeast(r, buf, len(buf))
}
io.ReadFull本質上面調用了io.ReadAtLeast,在此再也不贅述。