net.Dial仅发起底层连接尝试,参数错误会直接报错而非连接失败;必须用net.DialContext配context.WithTimeout设可控超时,且需调用SetRead/WriteDeadline确保通信健壮。

net.Dial 不是万能的“拨号按钮”,它只做一件事:发起一次底层连接尝试。用错参数、不设超时、忽略错误类型,轻则连不上,重则卡死 goroutine。
network 参数写错会直接报 lookup 错误,不是连不上
常见误区是把 "tcp4" 或 "tcp6" 当成更“安全”的写法——其实它们是独立网络类型,不是 "tcp" 的子集。
-
net.Dial("tcp4", "example.com:80")会尝试仅用 IPv4 解析域名,但错误信息却是dial tcp4: lookup tcp4: no such host,误导你去查 DNS -
"tcp"才是通用写法,它自动尝试 IPv4/IPv6(按系统配置顺序),失败后才报真实原因(如connection refused) - UDP 同理:
"udp"可用,"udp4"也可用,但混用 network 和 address 版本(比如"udp4"+ IPv6 地址)会 panic
address 格式漏冒号或端口非法,错误发生在解析阶段
地址不是字符串拼接,而是严格按协议解析。出错不进网络栈,根本不会发 SYN 包。
-
"127.0.0.1:8080"✅,"localhost:8080"✅(依赖本地 hosts 或 DNS) -
"127.0.0.18080"❌ →address 127.0.0.18080: missing port in address -
"[::1]:8080"✅(IPv6),但"::1:8080"❌ →invalid port - Unix socket 路径必须绝对:
"/tmp/sock"✅,"tmp/sock"❌ →connect unix tmp/sock: no such file or directory
默认无可靠超时,线上必须用 net.DialContext
net.Dial 自身没有超时控制,依赖操作系统默认行为(Linux 通常 30 秒左右),且不可取消。别用已弃用的 net.DialTimeout。
- 正确姿势是:
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second),然后net.DialContext(ctx, "tcp", addr) - 超时后
err是*net.OpError类型,需断言判断:if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { ... } - 即使
DialContext成功返回conn,也不代表链路可用——第一次Write仍可能触发write: broken pipe,读写前务必调用conn.SetDeadline或SetRead/WriteDeadline
UDP Dial 后不是“建立连接”,而是绑定远端地址
UDP 是无连接协议,net.Dial("udp", ...) 实际上只是创建一个已绑定远端地址的 Conn,简化后续 Write/Read,不发送任何数据包。
立即学习“go语言免费学习笔记(深入)”;
- 它等价于先
net.ListenUDP再conn.WriteTo,但封装了远端地址记忆 - 若远端不可达,错误不会在
Dial时暴露,而是在首次Write或Read时返回io.EOF或connection refused(ICMP Port Unreachable) - UDP
Conn不能用conn.RemoteAddr()判断是否“连通”,只能靠业务层心跳或响应确认


















