這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
工作的原因接觸到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)