這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
工作的原因接觸到socket,要使用socket實現一個長串連。之前只在C/C++上用過socket,而且在學校的時候也只是簡單的做聊天室小程式,並沒有涉及到長串連。突然用到工作項目上,心裡還是有點懸的,畢竟網上的資料也不是很多。這裡就一步一步記錄下學習的過程、遇到的問題以及最重要的——解決問題的方法。
一、說明
golang中的socket需要用到net包,裡面封裝好了很多常用的函數方法以及元素類型。而且無需像C一樣要經曆socket,bind,listen,accept等等流程,只需要簡單的幾步流程就能完成,非常方便。
二、IP類型
net包中定義的ip類型直接就是byte數組:
我們可以使用func parseIP(s string) IP 來把一個ip地址轉換成IP類型:
1 2 3 4 5 6 7
|
ipAddr := "192.168.1.79" addr := net.ParseIP(ipAddr) if addr == nil{ fmt.Println("unavaliable addr") }else{ fmt.Println(addr.To16()) }
|
三、函數
3.1 funcResolveTCPAddr(net, addr string) (*TCPAddr, error)
ResolveTCPAddr 函數的功能是解析TCP串連的地址,包含ip 和port
net:tcp tcp4 tcp6三選一,分別表示TCPv4,TCPv6和任意,預設是tcp4
addr:主機的地址,可以是[ip+port],也可以是[domain+port],可以省略主機部分,表示本機地址
返回一個*TCPAddr 類型 ,表示一個TCP串連地址:
1 2 3 4 5
|
type TCPAddr struct { IP IP Port int Zone string // IPv6 scoped addressing zone }
|
3.2 func ResolveIPAddr(net, addr string) (*IPAddr, error)
ResolveIPAddr 函數的功能是解析ip地址:
net:ip ip4 ip6 分別代表IPv4,IPv6以及任意。預設留空表示ip4
addr :IP地址 返回一個*IPAddr 結構:
1 2 3 4
|
type IPAddr struct { IP IP Zone string // IPv6 scoped addressing zone }
|
3.3 func Dial(network, address string) (Conn, error)
Dial 函數的功能是建立一個串連:
network: 如果是TCP串連,對應tcp tcp4 tcp6;如果是IP串連,對應ip ip4 ip6。對於ip串連,需要在後面加一個冒號然後註明協議號或者協議名字
address:串連的地址,ip+port或domain+port 形式,也可以省略主機地址表示本地地址217.0.0.1
返回一個net.Conn 介面對象,包含了串連的資訊,我們可以使用該對象的Write() 和Read() 對串連進行讀寫。
1 2 3 4 5 6 7 8 9 10
|
type Conn interface { Read(b []byte) (n int, err error) Write(b []byte) (n int, err error) Close() error LocalAddr() Addr RemoteAddr() Addr SetDeadline(t time.Time) error SetReadDeadline(t time.Time) error SetWriteDeadline(t time.Time) error }
|
與這個函數相對應的兩個函數:
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error)
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error)
分別表示建立TCP請求和IP請求,中間多的laddr 表示本地的地址,一般為nil 。
3.4 func (c *conn) Write(b []byte) (int, error)
向conn 連線物件中寫入資料,即發送資料給對方,寫入的資料是[]byte 類型,成功將返回傳送的資料包位元組數。
1 2 3 4 5
|
n, err := tcpCoon.Write([]byte("HelloWorld")) if err != nil{ fmt.Println(err) return }
|
3.5 func (c *conn) Read(b []byte) (int, error)
從conn連線物件中讀取資料,成功將返回讀取到的位元組數。
1 2 3 4 5 6 7
|
recvData := make([]byte, 2048) n, err = tcpCoon.Read(recvData) if err != nil{ fmt.Println(err) return } fmt.Println(string(recvData))
|
3.6 func Listen(net, laddr string) (Listener, error)
Listen函數在服務端使用,讓服務端開始監聽。
net :和上面一樣,可以是tcp ip相關的值
laddr :要監聽的地址,ip+port省略主機地址將使用本機地址127.0.0.1
相應的兩個函數:
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error):監聽TCP串連
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error):監聽IP串連
3.7 func (l *TCPListener) Accept() (Conn, error)
服務端開始監聽需要使用Accept 函數來接受用戶端串連,此時服務端將進入阻塞狀態。
相應的還有一個
func (l *TCPListener) AcceptTCP() (*TCPConn, error)