golang源码解读之 net.Dial

1、调用 例子

conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
   
	// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
status, err := bufio.NewReader(conn).ReadString('\n')
// ...

源码dial.go文件中没有实际发起连接,主要是对一些参数进行预处理,比如:解析网络类型、从addr解析ip地址,而实际发起连接的函数在tcpsock_posix.go、udpsock_posix.go。
2、net.Dial的源码解读

// net.Dial函数解读
//  实际是对Dialer.Dial的一个封装, 封装后,可以直接调用Dial拨号,而不需要再去定义一个Dialer结构体对象,利用对象拨号,省去了定义结构体对象,封装时用的同名方法,便于记忆
func Dial(network, address string) (Conn, error) {
   
	var d Dialer      // 定义了一个 Dialer结构体对象,使用该对象的Dial方法去拨号,所以net.Dial 实际是对Dialer.Dial的一个封装
	return d.Dial(network, address)
}

3、Dialer.DialContext 解读
d.DialContext()可以传入一个context,如果context的生命周期在connect完成之前结束,那么会立即返回错误。如果context在连接建立完成之后结束,则不会影响连接。另外如果addr是一组ip地址的话,会把当前剩下的所有时间均分到每个ip上去尝试连接。只要有一个成功,就会立即返回成功的连接并取消其他尝试

// Dialer.DialContext 解读
// DialContext是结构体Dialer对象的原始拨号方法,入参三个(ctx上下文用于设置拨号对象的上下文截止时间、)
// DialContext使用提供的上下文连接到指定网络上的地址, 上下文必须非0且不能过期
// 主机的每个ip链接时间 是timeout/n,n是多少个ip,
func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) {
   
	// 1、首先判断参数,上下文为空报错
	if ctx == nil {
   
		panic("nil context")
	}
	// 2、获取dialer和上下文的最早的deadline, (ctx上下文用于设置拨号对象的上下文截止时间、time.now()用于计算设置timeout字段时候的截止时间)
	deadline := d.deadline(ctx, time.Now())
	if !deadline.IsZero() {
   
		// 当计算出了截止时间:如果上下文没有截止时间、或者最终截止时间早于上下文截止时间
		if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
   
			// 就将最终截止时间 设置成 上下文的截止时间处理, 还会返回一个cancel函数,在最后调用
			subCtx, cancel := context.WithDeadline(ctx, deadline)
			defer cancel()
			// 构建上下文:返参 subCtx就是父上下文的一个副本,现在赋值回去
			ctx = subCtx
		}
	}
	// 3、拨号对象 调用取消通道读值,如果有取消值
	if oldCancel := d.Cancel; oldCancel != nil {
   
		// 就对上下文取消操作:WithCancel 返回具有新cancel通道的父级的副本
		subCtx, cancel := context.WithCancel(ctx)
		defer cancel()   // 一旦在此上下文中运行的操作完成,就调用cancel

		// 开启协程判断 拨号对象的取消通道读值,和上下文完成的工作应取消时,Done返回一个关闭的通道读值,任何一个先完成,都会进行取消操作
		go func() {
   
			select {
   
			case <-oldCancel:
				cancel()
			case <-subCtx.Done():
			}
		}()
		// 将副本 赋值给 父上下文
		ctx = subCtx
	}

	// 4、在解析期间对nettrace(如果有的话)进行阴影处理,这样就不会为DNS查找触发连接事件
	// 调用	import "internal/nettrace",TraceKey{}是一个上下文得键key, 关联值应该是*Trace结构, 取出值后类型断言成*nettrace.Trace类型
	resolveCtx := ctx
	if trace, _ := ctx.Value(nettrace.TraceKey{
   }).(*nettrace.Trace); trace != nil {
   
		shadow := *trace
		// 设置在拨号之前之后都不调用该上下文的值
		shadow.ConnectStart = nil
		shadow.ConnectDone = nil
		// 返回父级的副本,提供的键必须可比较、用户需要自己定义类型
		resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{
   }, &shadow)
	}

	// 5、 设置解析器, 并根据解析器返回地址列表,
	// d.resolver()方法:如果设置了解析器就返回,没设置就返回默认解析器
	// resolveAddrList() 方法: 调用parseNetwork解析出string的网络协议,根据不同网络类型分别调用ResolveUnixAddr,和internetAddrList方法解析出地址(文字IP地址或DNS名称).</
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
sync.Map 是 Go 语言标准库中提供的一种并发安全的字典类型,它可以被多个 goroutine 安全地访问和修改。在多个 goroutine 中并发地读写一个 map 时,会出现竞争条件,从而导致数据不一致。而 sync.Map 利用了一些锁的技巧,避免了这种竞争条件的发生,从而实现了高效的并发安全访问。 sync.Map 的 API 非常简单,主要包括以下几个方法: 1. Store(key, value interface{}):将一个键值对存储到 sync.Map 中。 2. Load(key interface{}) (value interface{}, ok bool):根据键从 sync.Map 中获取对应的值。 3. LoadOrStore(key, value interface{}) (actual interface{}, loaded bool):如果键存在于 sync.Map 中,则返回对应的值和 true,否则将键值对存储到 sync.Map 中并返回新的值和 false。 4. Delete(key interface{}):从 sync.Map 中删除一个键值对。 5. Range(f func(key, value interface{}) bool):遍历 sync.Map 中的键值对,并对每一个键值对调用函数 f,如果 f 返回 false,则停止遍历。 下面是一个使用 sync.Map 的简单例子,展示了如何在多个 goroutine 中并发地访问和修改 sync.Map: ``` package main import ( "fmt" "sync" ) func main() { var m sync.Map var wg sync.WaitGroup wg.Add(2) // goroutine 1: 向 sync.Map 中存储键值对 go func() { defer wg.Done() m.Store("key1", "value1") m.Store("key2", "value2") }() // goroutine 2: 从 sync.Map 中加载键值对 go func() { defer wg.Done() if v, ok := m.Load("key1"); ok { fmt.Println("value for key1:", v) } if v, ok := m.Load("key2"); ok { fmt.Println("value for key2:", v) } }() wg.Wait() } ``` 在上面的例子中,我们首先创建了一个 sync.Map 对象 m。然后在两个 goroutine 中同时访问这个对象,一个 goroutine 向其中存储键值对,另一个 goroutine 则从其中加载键值对。由于 sync.Map 是并发安全的,所以这两个 goroutine 可以并发地访问和修改 sync.Map,而不会出现竞争条件。 需要注意的是,虽然 sync.Map 是并发安全的,但它并不是用来替代普通的 map 的。如果你只是需要在某个 goroutine 中访问和修改一个 map,那么你应该使用普通的 map,因为 sync.Map 的性能会比较差。只有在需要多个 goroutine 并发地访问和修改一个 map 时,才应该考虑使用 sync.Map。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值