Go Net包的一些使用
阿新 • • 發佈:2021-01-13
技術標籤:計算機網路
Golang net包
最近在專案日誌報警中,經常看到"broken pipe"這個報錯,經過詢問負責基礎架構的同事,是server正在處理的時候client主動關閉TCP連線導致的,可以忽略;於是我們的做法是,將這個報警項新增進了白名單。但本著刨根問底的心態,我自己按照網上的文章也嘗試本地復現了一下這個報錯。
1 Conn
Conn是一個基本的介面型別,以資料流為嚮導的網路連線接。注意是介面型別,不需要我們來手動構造實現Conn介面,生成滿足Conn介面的函式。
func Dial(network, address string) (Conn, error) // 用來建立TCP連線 func DialTimeout(network, address string, timeout time.Duration) (Conn, error) func FileConn(f *os.File) (c Conn, err error) func Pipe() (Conn, Conn)
服務端通過Accept()
方法來接受Listen到的一個TCP連線;
客戶端通過Dial()
方法來請求建立一個TCP連線。
在連線建立完成後,由於TCP是全雙工通訊的,所以兩端都可以使用conn.Read() conn.Write()
函式來讀取內容到buffer中。
2 測試TCP建立連線和通訊的過程
為了方便觀察,把Server做成只會讀,Client只會寫。
服務端程式碼
func recvConnectionMsg(connection net.Conn) {
buffer := make([]byte, 50)
defer connection.Close()
for {
n, err := connection.Read(buffer)
if err != nil { // 一直讀到連線關閉
fmt.Println("connection closed")
return
}
fmt.Println("recv msg: ", string(buffer[0:n]))
}
}
func main() {
listenSock, err := net.Listen("tcp", "localhost:10000")
if err != nil {
fmt.Println ("error: ", err)
os.Exit(1)
}
defer listenSock.Close()
for {
newConn, err := listenSock.Accept() // 獲取到一個流式的Listener
if err != nil {
continue
}
go recvConnectionMsg(newConn)
}
}
客戶端程式碼
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:10000")
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
defer conn.Close()
for {
time.Sleep(100 * time.Millisecond)
// 向TCP快取中寫入內容
n, err := conn.Write([]byte("hello world"))
if err != nil {
fmt.Printf("Error: %s, Index: %d\n", err, n)
}
}
}
3 Server異常場景
-
服務端程序崩潰
服務端程序崩潰後:
- 如果client正在進行讀操作,會讀到一個空的buffer;
- 如果client正在進行write操作,會引發一個
panic: broken pipe
。
-
服務端關機:
這裡一般指的是正常關機,系統會向各個程序傳送
SIGTERM
和SIGKILL
訊號,server程序會推出,然後關閉相應的檔案描述符,給對應client傳送FIN報文。 -
服務端重啟
- 如果客戶端在server shutdown之前沒有傳送資料,client是無法感知server shutdown的;
- 如果客戶端在重啟後繼續傳送資料,是無法使用原有的套接字傳送訊息的。