這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在Go的net包中定義了很多類型、函數和方法用來網路編程,其中IP的定義如下:
type IP []byte
輸入的IP地址需要解析,以免輸入的是錯誤格式的IP地址:
func ParseIP(s string) IP
參數s可以是字串類型的ipv4或ipv6地址,如果解析錯誤會返回nil。
在Go語言的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可以用在用戶端和伺服器端來讀寫資料。而TCP的地址資訊用TCPAddr來表示:
type TCPAddr struct { IP IP Port int}
通過ResolveTCPAddr擷取一個TCPAddr:
func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)
- net參數是"tcp4"、"tcp6"、"tcp"中的任意一個,分別表示TCP(IPv4-only),TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一個).
- addr表示網域名稱或者IP地址,例如"www.google.com:80" 或者"127.0.0.1:22".
通過net包中的DialTCP函數來建立一個TCP串連,並返回一個TCPConn類型的對象,當串連建立時伺服器端也建立一個同類型的對象,此時用戶端和伺服器段通過各自擁有的TCPConn對象來進行資料交換。一般而言,用戶端通過TCPConn對象將請求資訊發送到伺服器端,讀取伺服器端響應的資訊。伺服器端讀取並解析來自用戶端的請求,並返回應答資訊,這個串連只有當任一端關閉了串連之後才失效,不然這串連可以一直在使用。建立串連的函數定義如下:
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error)
- net參數是"tcp4"、"tcp6"、"tcp"中的任意一個,分別表示TCP(IPv4-only)、TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一個)
- laddr表示本機地址,一般設定為nil
- raddr表示遠端服務地址
用戶端代碼如下所示:
package mainimport ( "fmt" "io/ioutil" "net" "os")func main() { if len(os.Args) != 2 { fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0]) os.Exit(1) } service := os.Args[1] tcpAddr, err := net.ResolveTCPAddr("tcp4", service) checkError(err) conn, err := net.DialTCP("tcp", nil, tcpAddr) checkError(err) _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n")) checkError(err) result, err := ioutil.ReadAll(conn) checkError(err) fmt.Println(string(result)) os.Exit(0)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) }}
在伺服器端我們需要綁定服務到指定的非啟用連接埠,並監聽此連接埠,當有用戶端請求到達的時候可以接收到來自用戶端串連的請求。net包中有相應功能的函數,函數定義如下:
func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)func (l *TCPListener) Accept() (c Conn, err os.Error)
伺服器端代碼如下:
package mainimport ( "fmt" "net" "os" "time" "strconv")func main() { service := ":1200" tcpAddr, err := net.ResolveTCPAddr("tcp4", service) checkError(err) listener, err := net.ListenTCP("tcp", tcpAddr) checkError(err) for { conn, err := listener.Accept() if err != nil { continue } go handleClient(conn) }}func handleClient(conn net.Conn) { conn.SetReadDeadline(time.Now().Add(2 * time.Minute)) // set 2 minutes timeout request := make([]byte, 128) // set maxium request length to 128KB to prevent flood attack defer conn.Close() // close connection before exit for { read_len, err := conn.Read(request) if err != nil { fmt.Println(err) break } if read_len == 0 { break // connection already closed by client } else if string(request) == "timestamp" { daytime := strconv.FormatInt(time.Now().Unix(), 10) conn.Write([]byte(daytime)) } else { daytime := time.Now().String() conn.Write([]byte(daytime)) } request = make([]byte, 128) // clear last read content }}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) }}
TCP有很多串連控制函數,我們平常用到比較多的有如下幾個函數:
func DialTimeout(net, addr string, timeout time.Duration) (Conn, error)
設定建立串連的逾時時間,用戶端和伺服器端都適用,當超過設定時間時,串連自動關閉。
func (c *TCPConn) SetReadDeadline(t time.Time) errorfunc (c *TCPConn) SetWriteDeadline(t time.Time) error
用來設定寫入/讀取一個串連的逾時時間。當超過設定時間時,串連自動關閉。
func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error
設定用戶端是否和伺服器端保持長串連,可以降低建立TCP串連時的握手開銷,對於一些需要頻繁交換資料的應用情境比較適用。
Go語言套件中處理UDP Socket和TCP Socket不同的地方就是在伺服器端處理多個用戶端請求資料包的方式不同,UDP缺少了對用戶端串連請求的Accept函數。其他基本幾乎一模一樣,只是TCP換成了UDP而已。UDP的幾個主要函數如下所示:
func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err os.Errorfunc (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err os.Error)
UDP的用戶端代碼如下:
package mainimport ( "fmt" "net" "os")func main() { if len(os.Args) != 2 { fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0]) os.Exit(1) } service := os.Args[1] udpAddr, err := net.ResolveUDPAddr("udp4", service) checkError(err) conn, err := net.DialUDP("udp", nil, udpAddr) checkError(err) _, err = conn.Write([]byte("anything")) checkError(err) var buf [512]byte n, err := conn.Read(buf[0:]) checkError(err) fmt.Println(string(buf[0:n])) os.Exit(0)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error ", err.Error()) os.Exit(1) }}
伺服器端的代碼如下:
package mainimport ( "fmt" "net" "os" "time")func main() { service := ":1200" udpAddr, err := net.ResolveUDPAddr("udp4", service) checkError(err) conn, err := net.ListenUDP("udp", udpAddr) checkError(err) for { handleClient(conn) }}func handleClient(conn *net.UDPConn) { var buf [512]byte _, addr, err := conn.ReadFromUDP(buf[0:]) if err != nil { return } daytime := time.Now().String() conn.WriteToUDP([]byte(daytime), addr)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error ", err.Error()) os.Exit(1) }}
可見TCP與UDP的conn對象是不同的,前者是net.Conn,後者是net.UDPConn。另外UDP的伺服器端的讀寫是要帶著地址的,這個地址是用戶端的地址。