osi參考模型將計算機網絡結構分爲7個層次,可是在實際的開發應用中,咱們更加承認TCP/IP族協議的五層結構,即應用層(http、ftp、dns),傳輸層(udp、tcp),網絡層(ip),鏈路層(以太網),物理層。golang
socket編程做爲一種基於網絡層和傳輸層的數據io模式主要分爲兩種,TCP Socket和UDP Socket,也即面向鏈接的流式Socket和麪向無鏈接的數據報式Socket。編程
今天主要和你們講講golang的TCP Socket編程。先來看個簡單的例子吧服務器
server.go網絡
//模擬server端 func main() { tcpServer, _ := net.ResolveTCPAddr("tcp4", ":8080") listener, _ := net.ListenTCP("tcp", tcpServer) for { //當有新的客戶端請求來的時候,拿到與客戶端的鏈接 conn, err := listener.Accept() if err != nil { fmt.Println(err) continue } //處理邏輯 go handle(conn) } } func handle(conn net.Conn) { defer conn.Close() //讀取客戶端傳送的消息 go func() { response, _ := ioutil.ReadAll(conn) fmt.Println(string(response)) }() //向客戶端發送消息 time.Sleep(1 * time.Second) now := time.Now().String() conn.Write([]byte(now)) }
client.go數據結構
//模擬客戶端 func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0]) } //獲取命令行參數 socket地址 server := os.Args[1] addr, err := net.ResolveTCPAddr("tcp4", server) checkError(err) //創建tcp鏈接 conn, err := net.DialTCP("tcp4", nil, addr) checkError(err) //向服務端發送數據 _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n")) checkError(err) //接收響應 response, _ := ioutil.ReadAll(conn) fmt.Println(string(response)) os.Exit(0) } func checkError(err error) { if err != nil { fmt.Println(err) os.Exit(1) } }
總體大概的流程是:socket
server端先經過tcp協議監聽端口8080,以後當有客戶端來訪問時可以讀取信息而且寫入響應信息,注意此處 conn, err := listener.Accept() 語句是用來獲取下一個調用鏈接的,若是一直沒有鏈接,則會處於阻塞狀態。tcp
客戶端經過DialTCP嘗試與服務端創建鏈接,鏈接成功後向服務器端發送數據並接收響應,而且以上代碼中,只有等待服務器端關閉鏈接以後,該鏈接纔會失效,不然鏈接會一直處於ESTABLISHED狀態。以下圖是筆者將defer conn.Close()註釋掉以後,查看的8080端口的鏈接,會發現鏈接一直存在:計算機網絡
若是使用細心一點的話,在客戶端代碼執行結束以後,此時的服務器端鏈接並無釋放(注意下圖local address是8080端口),而是進入了TIME_WAIT狀態:命令行
一開始筆者覺得是鏈接沒有正確釋放的緣由,後來查看了下TCP四次揮手有關的內容才弄明白這塊的緣由,由於是服務器端主動斷開鏈接,因此在服務器端發送完最後一個ACK以後,會進入TIME_WAIT狀態等待一段時間,防止ACK沒有發送成功,等待時間一過,系統會自動釋放鏈接。以下是一個簡單的狀態轉變圖(不是特別精準,只看斷開鏈接部分),但願能對讀者有所幫助:code
詳細的四次揮手的各個狀態輪轉的變化,能夠去谷歌搜索一下。
PS:以上是一個簡單的socket編程的示例,只包含了一對client和server,那若是一個server有不少個client,此時有一個client但願可以將消息發送給除它本身以外的全部client fd怎麼辦?
其實這個問題能夠經過server端來作,首先server端定義一種數據結構,不管是map仍是slice,而後將全部的有效鏈接都放入其中,若是有client fd發送消息過來,遍歷該結構,將消息發送給每個非當前client的fd,由此解決該問題。