Golang TCP網絡編程的具體實現(xiàn)
網絡編程介紹
網絡編程介紹
- 網絡編程是指通過計算機網絡實現(xiàn)程序間通信的一種編程技術,涉及到在不同計算機之間建立連接、傳輸數據和協(xié)議解析等操作。
- 套接字(Socket)編程是網絡編程的一種實現(xiàn)方式,其提供了一種機制,使得應用程序能夠通過網絡進行數據傳輸和通信。
- Go中的net包是標準庫中提供的網絡編程包,是基于套接字編程的一種實現(xiàn)方式,提供了對TCP、UDP、IP、ICMP、Unix域套接字等常見網絡協(xié)議的支持,通過net包可以完成創(chuàng)建套接字、建立連接、發(fā)送和接收數據等操作,實現(xiàn)網絡通信。
TCP網絡編程
服務器監(jiān)聽
服務器監(jiān)聽
在Go的net包中,Listen函數用于創(chuàng)建并返回一個網絡監(jiān)聽器(Listener),以監(jiān)聽指定網絡地址和端口上的連接請求。該函數的函數原型如下:
func Listen(network, address string) (Listener, error)
參數說明:
- network:用于指定網絡類型,其值必須是"tcp", “tcp4”, “tcp6”, “unix"或"unixpacket”。
- address:用于指定需要被監(jiān)聽的IP地址和端口號,格式為"host:port"。
返回值說明:
- 第一個返回值:表示創(chuàng)建的網絡監(jiān)聽器。
- 第二個返回值:如果創(chuàng)建網絡監(jiān)聽器過程中出錯,將返回非nil的錯誤值。
通過Listen函數創(chuàng)建得到的網絡監(jiān)聽器是Listener類型的,該類型是一個接口類型,其定義如下:
type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (Conn, error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. Addr() Addr }
Listener接口中各方法說明:
- Accept方法:從底層獲取下一個已經建立好的連接給監(jiān)聽器。
- Close方法:關閉監(jiān)聽器。
- Addr方法:返回監(jiān)聽器對應的網絡地址(由IP地址和端口號組成)。
當使用Listen函數創(chuàng)建TCP類型的監(jiān)聽器時,其返回的監(jiān)聽器底層具體的類型是TCPListener,其定義如下:
type TCPListener struct { fd *netFD lc ListenConfig }
TCPListener結構體各字段說明:
- fd:對底層網絡文件描述符的封裝,提供了對網絡連接的讀寫和控制操作。
- lc:用于配置監(jiān)聽器創(chuàng)建的行為,比如設置監(jiān)聽地址、控制網絡參數等。
TCPListener結構體中的fd字段是netFD類型的,其定義如下:
type netFD struct { pfd poll.FD // immutable until Close family int sotype int isConnected bool // handshake completed or use of association with peer net string laddr Addr raddr Addr }
netFD結構體各字段說明:
- pfd:用于與底層的操作系統(tǒng)文件描述符進行交互。
- family:表示套接字的協(xié)議家族,比如IPv4或IPv6。
- sotype:表示套接字的類型,比如TCP或UDP
- isConnected:表示連接是否已完成握手或與對方建立關聯(lián)。
- net:表示網絡協(xié)議,比如"tcp"或"udp"。
- laddr:表示本地網絡連接的地址。
- raddr:表示遠程網絡連接的地址。
服務器監(jiān)聽示例
服務器監(jiān)聽示例如下:
package main import ( "fmt" "net" ) func main() { // 服務器監(jiān)聽 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") }
說明一下:
- 服務器在創(chuàng)建監(jiān)聽套接字時,將其綁定到
0.0.0.0
(通常表示為INADDR_ANY)地址,這樣服務器就可以同時監(jiān)聽和接受來自不同網絡接口的連接請求,而不需要為每個接口分別創(chuàng)建監(jiān)聽套接字。 - 為了避免網絡文件描述符資源泄露,在創(chuàng)建監(jiān)聽器后及時利用defer機制關閉監(jiān)聽器。監(jiān)聽器被關閉后會停止監(jiān)聽新的連接請求,并且任何被阻塞的Accept操作都會被解除阻塞并返回錯誤。
客戶端連接服務器
客戶端連接服務器
在Go的net包中,Dial函數用于客戶端應用程序與遠程服務器建立連接。該函數的函數原型如下:
func Dial(network, address string) (Conn, error)
參數說明:
- network:用于指定網絡協(xié)議,比如"tcp", "udp"等。
- address:用于指定連接的目標地址。
返回值說明:
- 第一個返回值:表示建立的網絡連接。
- 第二個返回值:如果建立網絡連接過程中出錯,將返回非nil的錯誤值。
通過Dial函數建立得到的連接Conn類型的,該類型是一個接口類型,其定義如下:
type Conn interface { // Read reads data from the connection. Read(b []byte) (n int, err error) // Write writes data to the connection. Write(b []byte) (n int, err error) // Close closes the connection. // Any blocked Read or Write operations will be unblocked and return errors. Close() error // LocalAddr returns the local network address, if known. LocalAddr() Addr // RemoteAddr returns the remote network address, if known. RemoteAddr() Addr // SetDeadline sets the read and write deadlines associated // with the connection. It is equivalent to calling both // SetReadDeadline and SetWriteDeadline. SetDeadline(t time.Time) error // SetReadDeadline sets the deadline for future Read calls // and any currently-blocked Read call. SetReadDeadline(t time.Time) error // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. SetWriteDeadline(t time.Time) error }
Conn接口中各方法說明:
- Read方法:從連接中讀取數據。
- Write方法:向連接中寫入數據。
- Close方法:關閉連接。
- LocalAddr方法:返回連接對應的本地網絡地址(由IP地址和端口號組成)。
- RemoteAddr方法:返回連接對應的遠程網絡地址(由IP地址和端口號組成)。
- SetDeadline方法:設置連接讀取和寫入的截止時間。
- SetReadDeadline方法:設置連接讀取的截止時間。
- SetWriteDeadline方法:設置連接寫入的截止時間。
當使用Dial函數與TCP服務器建立連接時,其返回的網絡連接底層具體的類型是TCPConn,其定義如下:
type TCPConn struct { conn }
TCPConn結構體中僅嵌套了一個conn類型的匿名結構體,其定義如下:
type conn struct { fd *netFD }
可以看到,conn結構體中的fd字段與TCPListener結構體中的fd字段的類型相同,它們都是對底層網絡文件描述符的封裝,提供了對網絡連接的讀寫和控制操作。
客戶端連接服務器示例
客戶端連接服務器示例如下:
package main import ( "fmt" "net" ) func main() { // 客戶端連接服務器 conn, err := net.Dial("tcp", "127.0.0.1:8081") if err != nil { fmt.Printf("connect server error, err = %v\n", err) return } defer conn.Close() fmt.Println("connect server success...") }
說明一下:
- 當客戶端連接TCP服務器時,服務器必須處于監(jiān)聽狀態(tài)。
- 為了避免網絡文件描述符資源泄露,客戶端在與服務器建立連接后及時利用defer機制關閉連接。
服務端獲取連接
服務端獲取連接
在創(chuàng)建TCP網絡監(jiān)聽器后,調用Listener接口的Accept方法,本質調用的是TCPListener的Accept方法,該方法用于從底層獲取下一個已經建立好的連接給監(jiān)聽器,如果底層沒有建立好的連接則會進行阻塞等待。該方法的原型如下:
func (l *TCPListener) Accept() (Conn, error)
返回值說明:
- 第一個返回值:表示獲取到的與客戶端建立好的連接。
- 第二個返回值:如果在獲取連接過程中出錯,將返回非nil的錯誤值。
服務端獲取連接示例
服務端獲取連接示例如下:
package main import ( "fmt" "net" ) func process(conn net.Conn) { defer conn.Close() fmt.Printf("handle a link %v...\n", conn.RemoteAddr()) } func main() { // 服務器監(jiān)聽 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") for { fmt.Println("waiting client connect...") // 服務端獲取連接 conn, err := listen.Accept() if err != nil { fmt.Printf("accept error, err = %v\n", err) continue } fmt.Printf("get a link from %v...\n", conn.RemoteAddr()) // 開啟新協(xié)程為客戶端提供服務 go process(conn) } }
說明一下:
- 網絡監(jiān)聽器的任務就是不斷調用Accept方法,從底層獲取已經建立好的連接并為其提供服務,通常在獲取到一個連接后會開啟一個新協(xié)程為其提供服務,而主協(xié)程則繼續(xù)調用Accept方法獲取新的連接。
- 為了讓新協(xié)程能夠獲取需要被處理的連接,需要將對應的連接通過參數傳遞的方式,傳遞給協(xié)程對應的處理函數。此外,為了避免網絡文件描述符資源泄露,需要在處理函數中利用defer機制關閉連接,保證連接處理完畢后能夠及時關閉連接。
向連接中寫入數據
向連接中寫入數據
在創(chuàng)建TCP連接后,調用Conn接口的Write方法,本質調用的是TCPConn的Write方法,該方法用于向連接中寫入數據。該方法的原型如下:
func (c *TCPConn) Write(b []byte) (int, error)
參數說明:
- b:表示要寫入連接的數據。
返回值說明:
- 第一個返回值:表示實際寫入的字節(jié)數。
- 第二個返回值:如果在寫入數據過程中出錯,將返回非nil的錯誤值。
從連接中讀取數據
從連接中讀取數據
在創(chuàng)建TCP連接后,調用Conn接口的Read方法,本質調用的是TCPConn的Read方法,該方法用于從連接中讀取數據。該方法的原型如下:
func (c *TCPConn) Read(b []byte) (int, error)
參數說明:
- b:輸出型參數,用于存儲讀取到的數據。
返回值說明:
- 第一個返回值:表示實際讀取的字節(jié)數。
- 第二個返回值:如果在讀取數據過程中出錯,將返回非nil的錯誤值。
關閉連接/監(jiān)聽器
關閉連接/監(jiān)聽器
為了避免網絡文件描述符泄露,TCP網絡監(jiān)聽器和TCP連接在使用完畢后都需要及時將其關閉,對應調用的分別是TCPListener和TCPConn的Close方法,這兩個方法的原型如下:
func (l *TCPListener) Close() error func (c *TCPConn) Close() error
返回值說明:
- 如果在關閉監(jiān)聽器或關閉連接過程中出錯,將返回非nil的錯誤值。
簡易的TCP回聲服務器
效果展示
效果展示
為了演示使用net包實現(xiàn)網絡通信,下面實現(xiàn)了一個簡易的TCP回聲服務器,其功能如下:
- 服務端能夠同時處理多個客戶端的連接請求,在為每個客戶端提供服務時,能夠將各個客戶端發(fā)來的數據顯示在服務端,同時將客戶端發(fā)來的數據再發(fā)回給客戶端。
- 客戶端在連接到服務器后,能夠不斷從控制臺讀取用戶輸入的數據發(fā)送給服務端,并將服務端發(fā)來的數據顯示在客戶端,用戶在控制臺輸入exit能夠退出客戶端。
最終效果如下:
服務端處理邏輯
服務端處理邏輯
服務端處理邏輯如下:
- 主協(xié)程調用Listen函數完成服務器的監(jiān)聽后,通過監(jiān)聽器不斷調用Accept方法從底層獲取已經建立好的連接,并為每一個獲取到的連接創(chuàng)建一個新協(xié)程為其提供服務,而主協(xié)程則繼續(xù)獲取新的連接。
- 每個新協(xié)程在為其對應的連接提供服務時,通過調用連接的Read方法不斷讀取客戶端發(fā)來的數據,將數據其顯示在服務端,同時通過調用連接的Write方法將客戶端發(fā)來的數據再發(fā)回給客戶端。
- 每個新協(xié)程在為其對應的連接提供服務的過程中,如果從連接中讀取數據或向連接中寫入數據時出錯,或是客戶端退出,則通過調用連接的Close方法將對應的連接關閉。
服務端代碼如下:
package main import ( "fmt" "io" "net" ) func process(conn net.Conn) { defer conn.Close() data := make([]byte, 1024) for { // 1、讀取客戶端發(fā)來的數據 n, err := conn.Read(data) if err != nil { if err == io.EOF { fmt.Printf("client %v quit\n", conn.RemoteAddr()) } else { fmt.Printf("read client message error, err = %v\n", err) } return } fmt.Printf("client message[%v]: %v\n", conn.RemoteAddr(), string(data[:n])) // 2、發(fā)送數據給客戶端 len, err := conn.Write(data[:n]) if err != nil || len != n { fmt.Printf("send back message error, err = %v\n", err) return } } } func main() { // 服務器監(jiān)聽 listen, err := net.Listen("tcp", "0.0.0.0:8081") if err != nil { fmt.Printf("listen error, err = %v\n", err) return } defer listen.Close() fmt.Println("listen success...") for { fmt.Println("waiting client connect...") // 服務端獲取連接 conn, err := listen.Accept() if err != nil { fmt.Printf("accept error, err = %v\n", err) continue } fmt.Printf("get a link from %v...\n", conn.RemoteAddr()) // 開啟新協(xié)程為客戶端提供服務 go process(conn) } }
說明一下:
- 當服務端從某個連接中讀取數據時,如果該連接對應的客戶端已經將連接關閉,那么服務端的讀操作將會返回io.EOF錯誤。
客戶端處理邏輯
客戶端處理邏輯
客戶端處理邏輯如下:
- 客戶端在調用Dial函數與服務端建立連接后,不斷讀取用戶的輸入,通過調用連接Write方法將用戶輸入的數據發(fā)送給服務端,然后通過調用連接的Read方法讀取服務端發(fā)來的數據并顯示在客戶端,如果用戶輸入exit則調用連接的Close方法將連接關閉。
客戶端代碼如下:
package main import ( "bufio" "fmt" "net" "os" ) func main() { // 客戶端連接服務器 conn, err := net.Dial("tcp", "127.0.0.1:8081") if err != nil { fmt.Printf("connect server error, err = %v\n", err) return } defer conn.Close() fmt.Println("connect server success...") reader := bufio.NewReader(os.Stdin) data := make([]byte, 1024) for { // 1、讀取用戶輸入 str, err := reader.ReadString('\n') if err != nil { fmt.Printf("read input error, err = %v\n", err) continue } str = str[:len(str)-2] // 去掉\r\n if str == "exit" { fmt.Printf("exit success...") break } // 2、發(fā)送數據給服務端 n, err := conn.Write([]byte(str)) if err != nil || n != len(str) { fmt.Printf("send message error, err = %v\n", err) continue } fmt.Printf("send %d byte message to server...\n", n) // 3、讀取服務端發(fā)來的數據 n, err = conn.Read(data) if err != nil { fmt.Printf("read message error, err = %v\n", err) continue } fmt.Printf("server message: %v\n", string(data[:n])) } }
說明一下:
- Stdin、Stdout和Stderr是os包中的全局變量,分別表示標準輸入流、標準輸出流和標準錯誤流。
- 客戶端在讀取用戶輸入時,通過bufio包中的Reader,以帶緩沖的方式每次從標準輸入流中讀取一行數據。
- Windows系統(tǒng)中通常使用
\r\n
作為換行符,因此客戶端在每次讀取一行用戶輸入的數據后需要將末尾的兩個字符去掉。
到此這篇關于Golang TCP網絡編程的具體實現(xiàn)的文章就介紹到這了,更多相關Golang TCP網絡編程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用go實現(xiàn)刪除sql里面的注釋和字符串功能(demo)
這篇文章主要介紹了使用go實現(xiàn)刪除sql里面的注釋和字符串功能,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11