Go 标准库 net


本篇文章主要介绍 Go 标准库中的 net 包,通过一个小例子介绍常用的 net 包函数/方法 Listen,Accept 和 Dial 等。

1. net 简介

Go 官网对 net 包的定义如下:

Package net provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets.

net 包的常用方法有 Listen, Accept 和 Dial 等。下面通过一段代码将他们组合起来:

package main

import (
	"fmt"
	"net"
	"sync"
)

var wg sync.WaitGroup

func serverAccept(l net.Listener) {
	defer wg.Done()
	ln, err := l.Accept()
	if err != nil {
		fmt.Printf("accept error")
		return
	}

	recData := make([]byte, 1024)
	len, err := ln.Read(recData)

	fmt.Printf("len: %d\n", len)
	fmt.Printf("rec Data: %v\n", string(recData[:len]))

	sendData := []byte("hello, client")
	ln.Write(sendData)
}

func main() {
	// server emulator
	l, err := net.Listen("tcp", ":0")
	if err != nil {
		fmt.Printf("listen error")
		return
	}

	wg.Add(1)
	go serverAccept(l)

	// client emulator
	addr := l.Addr().String()
	cn, err := net.Dial("tcp", addr)
	if err != nil {
		fmt.Printf("Dial error")
		return
	}

	data := []byte("hello, server")
	cn.Write(data)

	recDataClient := make([]byte, 1024)
	len, err := cn.Read(recDataClient)
	fmt.Printf("recDataClient: %v", string(recDataClient[:len]))

	wg.Wait()
}

这段代码实现了一个 server emulator 和 client emulator,client 发送 hello, server 给 server,server 再返回 hello, client 给 client。查看代码执行结果:

len: 13
rec Data: hello, server
recDataClient: hello, client

下面介绍代码使用到的 net 包函数/方法。

1.1 net.Listen

调用 Listen 将使 server 侦听在网络地址上。Listen 是 net 包中的函数,它的输入是 network 和 address,返回值是接口类型 Listener:

func Listen(network, address string) (Listener, error) {
	var lc ListenConfig
	return lc.Listen(context.Background(), network, address)
}

查看 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
}

它包括三种方法 Accept,Close 以及 Addr。查看 Accept 方法如下:

1.2 Listener.Accept

查看官网对 Accept 方法的定义:

Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.

它有两种原型:

func (l *TCPListener) Accept() (Conn, error)

func (l *UnixListener) Accept() (Conn, error)

Accept 的指针接收者为 TCPListener,代码中调用 Accept 的是接口类型 Listener。所以应该发生了 TCPListener 到接口类型的转换,转换过程就发生在 lc.Listen(...) 函数里,查看该函数:

func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) {
    ...
    var l Listener
        la := addrs.first(isIPv4)
        switch la := la.(type) {
        case *TCPAddr:
            l, err = sl.listenTCP(ctx, la)
        case *UnixAddr:
            l, err = sl.listenUnix(ctx, la)
    ...
}

func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
	...
}

发现方法 listenTCP,listenUnix 做了接口类型的转换。

1.3 net.Dial

client 调用 Dial 函数和 server 建立连接,Dial 返回 Conn 接口类型。

func Dial(network, address string) (Conn, error) {
	var d Dialer
	return d.Dial(network, address)
}

1.4 Conn

Conn 是一种接口类型,查看它的定义:

type Conn interface {
	// Read reads data from the connection.
	// Read can be made to time out and return an error after a fixed
	// time limit; see SetDeadline and SetReadDeadline.
	Read(b []byte) (n int, err error)

	// Write writes data to the connection.
	// Write can be made to time out and return an error after a fixed
	// time limit; see SetDeadline and SetWriteDeadline.
	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() Addr

	RemoteAddr() Addr

	SetDeadline(t time.Time) error

	SetReadDeadline(t time.Time) error

	SetWriteDeadline(t time.Time) error
}

代码中使用到了接口的 Read 和 Write 方法,它的功能如注释所示。其中 Read 和 Write 的入参是字节切片。

1.5 字节切片 []byte

Go 中定义了字节切片,字符切片的特性如下:

  1. 一个字符串是一个不可改变的字节序列
  2. 文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列
  3. 内置的len函数可以返回一个字符串中的字节数目
  4. 第i个字节并不一定是字符串的第i个字符,因为对于非ASCII字符的UTF8编码会要两个或多个字节
  5. 字符串操作基于原始字符串字节
  6. 字符串面值方式编写,只要将一系列字节序列包含在双引号内即可,解释字符串,其中的相关的转义字符将被替换;反引号括起来,支持换行,非解释字符串
  7. 每个符号都分配一个唯一的Unicode码点,Unicode码点对应Go语言中的rune整数类型
  8. UTF8是一个将Unicode码点编码为字节序列的变长编码
  9. 变长的编码无法直接通过索引来访问第n个字符
  10. 将字符串看作是字节(byte)的切片(slice)来实现对其标准索引法的操作

参考: https://cloud.tencent.com/developer/article/1501644

posted @ 2021-04-22 21:53  lubanseven  阅读(475)  评论(0编辑  收藏  举报