golang源码解读之 net.Dial

本文深入探讨了Golang中`net.Dial`的源码,包括参数预处理、`Dialer.DialContext`的功能,特别是如何处理context生命周期与连接建立的关系。此外,还分析了`dialParallel`和`dialSerial`的源码,以及`Dialer`对象的使用,如设置截止日期的方法。文章涵盖了网络类型、地址解析以及连接过程中的各种细节。
摘要由CSDN通过智能技术生成

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名称).</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值