Go tcp客户端、服务端编程
生活随笔
收集整理的這篇文章主要介紹了
Go tcp客户端、服务端编程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
內容參考net包,net包提供了可移植的網絡I/O接口,包括TCP/IP、UDP、域名解析和Unix域socket。
雖然本包提供了對網絡原語的訪問,大部分使用者只需要Dial、Listen和Accept函數提供的基本接口;以及相關的Conn和Listener接口。crypto/tls包提供了相同的接口和類似的Dial和Listen函數。
服務端
在服務器端我們需要綁定服務到指定的非激活端口, 并監聽此端口;當有客戶端請求到達的時候可以接收到來自客戶端連接的請求。
package mainimport ("fmt""net""os""strings""time" )func main() {// 監聽的服務器端口service := "127.0.0.1:1200"tcpAddr, err := net.ResolveTCPAddr("tcp4", service)if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 在服務器端我們需要綁定服務到指定的非激活端口, 并監聽此端口,// 當有客戶端請求到達的時候可以接收到來自客戶端連接的請求//// func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error)// ListenTCP在本地TCP地址laddr上聲明并返回一個*TCPListener, net參數必須是"tcp", "tcp4", "tcp6",// 如果laddr的端口字段為0, 函數將選擇一個當前可用的端口, 可以用Listener的Addr方法獲得該端口listener, err := net.ListenTCP("tcp", tcpAddr)if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}for {// 接受每一種請求// func (l *TCPListener) Accept() (Conn, error)// Accept用于實現Listener接口的Accept方法// 他會等待下一個呼叫, 并返回一個該呼叫的Conn接口conn, err := listener.Accept()// 當有錯誤發生的情況下最好是由服務端記錄錯誤, 然后當前連接的客戶端直接報錯而退出, 從而不會// 影響到當前服務端運行的整個服務if err != nil {continue}// 處理客戶端的連接go handleClient(conn)} }func handleClient(conn net.Conn) {// 超時時間, 當一定時間內客戶端無請求發送, conn便會自動關閉, 下面的for循環即會因為連接已關閉而跳出conn.SetReadDeadline(time.Now().Add(1 * time.Minute))// request在創建時需要指定一個最大長度以防止flood attack; 每次讀取到請求處理完畢后,// 需要清理request, 因為conn.Read()會將新讀取到的內容append到原內容之后// request 為提供的讀取的最長緩沖區, 緩沖區滿后會自動回寫到客戶端接收, 因此客戶端需要注意接收到數據的完整性后在處理// 但是也并非都是寫滿后才返回給客戶端, 有可能提前返回數據, 需要一個協議// 如果該緩沖區給的不夠, 可能造成客戶端傳遞過來的信息被截斷, 無法得到預期的結果request := make([]byte, 9)defer conn.Close()for {// 不斷讀取客戶端發來的請求, 由于我們需要保持與客戶端的長連接, 所以不能在讀取完一次請求后就關閉連接// 將讀取到的數據追加保存到request中readLen, err := conn.Read(request)if err != nil {fmt.Println(err)break}// 如果沒有讀取到客戶端任何信息, 則默認客戶端已經關閉if readLen == 0 {break} else if strings.TrimSpace(string(request[:readLen])) == "timestamp" {date := time.Now().Format("2006-01-02 15:04:05")conn.Write([]byte(date))} else {conn.Write([]byte(request[:readLen]))}// 每次發送寫完畢后都清除緩存空間, 防止追加request = make([]byte, 8)} }客戶端
首先程序將用戶的輸入作為參數service傳入net.ResolveTCPAddr獲取一個tcpAddr,然后把tcpAddr傳入DialTCP后創建了一個TCP連接conn,通過conn來發送請求信息,最后通過ioutil.ReadAll從conn中讀取全部的文本,也就是服務端響應反饋的信息。
package mainimport ("fmt""net""os""io/ioutil" )func main() {// 獲取提供服務的服務器ip地址service := os.Args[1]// func ResolveTCPAddr(net, addr string) (*TCPAddr, error)// ResolveTCPAddr獲取一個TCPAddr, 一個TCPAddr類型, 他表示一個TCP的地址信息// ResolveTCPAddr將addr作為TCP地址解析并返回// 參數addr格式為"host:port"或"[ipv6-host%zone]:port", 解析得到網絡名和端口名// net必須是"tcp", "tcp4"或"tcp6"tcpAddr, err := net.ResolveTCPAddr("tcp4", service)if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 通過net包中的DialTCP函數來建立一個TCP連接, 并返回一個TCPConn類型的對象// 當連接建立時服務器端也創建一個同類型的對象, 此時客戶端和服務器端通過各自擁// 有的TCPConn對象來進行數據交換//// 一般而言, 客戶端通過TCPConn對象將請求信息, 發送到服務器端, 讀取服務器端響應的信息// 服務器端讀取并解析來自客戶端的請求并返回應答信息, 這個連接只有當任一端關閉了連接之后才失效,// 不然這連接可以一直在使用//// func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)// DialTCP在網絡協議net上連接本地地址laddr和遠端地址raddr// net必須是"tcp", "tcp4", "tcp6"// 如果laddr不是nil, 將使用它作為本地地址, 否則自動選擇一個本地地址conn, err := net.DialTCP("tcp", nil, tcpAddr)if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 向連接的服務器端寫入信息// net包中有一個類型TCPConn, 這個類型可以用來作為客戶端和服務器端交互的通道, 他有兩個主要的函數:// func (c *TCPConn) Write(b []byte) (n int, err os.Error)// func (c *TCPConn) Read(b []byte) (n int, err os.Error)// TCPConn可以用在客戶端和服務器端來讀寫數據//_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))_, err = conn.Write([]byte("timestamp"))if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}_, err = conn.Write([]byte("a~~~"))if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}_, err = conn.Write([]byte("b~~~~~"))if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}_, err = conn.Write([]byte("c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"))if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 當客戶端停止寫入時, 需要告訴服務端, 信息發送終止, 服務端就返回全部的數據if err = conn.CloseWrite(); err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 讀取服務器端響應的全部內容// ReadAll從r讀取數據直到EOF或遇到error, 返回讀取的數據和遇到的錯誤// 成功的調用返回的err為nil而非EOF, 因為本函數定義為讀取r直到EOF, 它不會將讀取返回的EOF視為應報告的錯誤result, err := ioutil.ReadAll(conn)if err != nil {fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())os.Exit(1)}// 輸出獲取到的內容fmt.Println(string(result))os.Exit(0) }服務端啟動
服務端每次請求結束后都會以文件結束符標志輸出。
zhgxun-pro:go zhgxun$ go run server.go EOF EOF EOF EOF EOF EOF客戶端輸出
當客戶端停止寫入時,服務端將全部數據返回。
zhgxun-pro:go zhgxun$ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:44a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:46a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:47a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:48a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:49a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$ go run client.go 127.0.0.1:1200 2017-09-17 21:07:50a~~~b~~~~~c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zhgxun-pro:go zhgxun$總結
以上是生活随笔為你收集整理的Go tcp客户端、服务端编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang bloom filter实
- 下一篇: Apache ab测试工具使用方法(无参