【函数原型】
func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
func Dial(network, address string) (Conn, error)
DialTCP与DialUDP 的参数及返回比较相似,其中TCPConn与UDPConn的定义如下
分别在tcpsock.go 与udpsock.go文件中
// TCPConn is an implementation of the Conn interface for TCP network
// connections.
type TCPConn struct {
conn
}
// UDPConn is the implementation of the Conn and PacketConn interfaces
// for UDP network connections.
type UDPConn struct {
conn
}
而conn 在之前的文章中提到过,是定义在net.go中的一个文件描述符
type conn struct {
fd *netFD
}
从上面可知,DIalTCP与DIalUDP返回的都是 conn类似的数据。
而func Dial(network, address string) (Conn, error) ,参数做了简化,返回结果是一个Conn类型。
Conn 属于接口类型,可以参考以前的文章【go语言 socket编程系列】Conn接口类型及简单服务器实现Read Write 和Close方法
即 conn类似的数据实现了Conn中的方法,
【net.Dial简介】
函数定义在源文件dial.go中,源码如下
func Dial(network, address string) (Conn, error) {
var d Dialer
return d.Dial(network, address)
}
简短两行代码
第一行定义了一个 Dialer类型的数据。
第二行 调用Dialer数据类型实现的Dial方法
先看Dialer类型数据,主要是一些选项,超时时间、取消链接之类的,后续详细展开。
type Dialer struct {
Timeout time.Duration
Deadline time.Time
LocalAddr Addr
DualStack bool
FallbackDelay time.Duration
KeepAlive time.Duration
Resolver *Resolver
Cancel <-chan struct{}
}
Dialer类型数据实现的Dial方法源码如下,在dial.go中
func (d *Dialer) Dial(network, address string) (Conn, error) {
return d.DialContext(context.Background(), network, address)
}
仅仅是调用了d.DialContext 方法,这样做除了函数名看起来简洁外还没领悟到其他的好处~。
其源码如下,还没细看。
func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) {
if ctx == nil {
panic("nil context")
}
deadline := d.deadline(ctx, time.Now())
if !deadline.IsZero() {
if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
subCtx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
ctx = subCtx
}
}
if oldCancel := d.Cancel; oldCancel != nil {
subCtx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-oldCancel:
cancel()
case <-subCtx.Done():
}
}()
ctx = subCtx
}
// Shadow the nettrace (if any) during resolve so Connect events don't fire for DNS lookups.
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)
}
addrs, err := d.resolver().resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
}
dp := &dialParam{
Dialer: *d,
network: network,
address: address,
}
var primaries, fallbacks addrList
if d.DualStack && network == "tcp" {
primaries, fallbacks = addrs.partition(isIPv4)
} else {
primaries = addrs
}
var c Conn
if len(fallbacks) > 0 {
c, err = dialParallel(ctx, dp, primaries, fallbacks)
} else {
c, err = dialSerial(ctx, dp, primaries)
}
if err != nil {
return nil, err
}
if tc, ok := c.(*TCPConn); ok && d.KeepAlive > 0 {
setKeepAlive(tc.fd, true)
setKeepAlivePeriod(tc.fd, d.KeepAlive)
testHookSetKeepAlive()
}
return c, nil
}
在dial.go中有个DialTimeout 函数,即带timeout参数。其实现代码如下,通过Dialer struct的 timeout实现
func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
d := Dialer{Timeout: timeout}
return d.Dial(network, address)
}
【net.Dial用法】
函数调用方法很简单,源码注释中直接有例子
Dial("tcp", "golang.org:http")
Dial("tcp", "192.0.2.1:http")
Dial("tcp", "198.51.100.1:80")
Dial("udp", "[2001:db8::1]:domain")
Dial("udp", "[fe80::1%lo0]:53")
Dial("tcp", ":80")