工作的原因接触到 socket,要使用 socket 实现一个长连接。之前只在 C/C++上用过 socket,而且在学校的时候也只是简单的做聊天室小程序,并没有涉及到长连接。突然用到工作项目上,心里还是有点悬的,毕竟网上的资料也不是很多。这里就一步一步记录下学习的过程、遇到的问题以及最重要的——解决问题的方法。
一、说明
golang 中的 socket 需要用到 net 包,里面封装好了很多常用的函数方法以及元素类型。而且无需像 C 一样要经历 socket,bind,listen,accept 等等流程,只需要简单的几步流程就能完成,非常方便。
二、 IP 类型
net 包中定义的 ip 类型直接就是 byte 数组:
1 |
type 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)
评论