进行错误处理,键入断言和网络包

Nea*_*eal 13 error-handling network-programming go

我正在学习并试图了解如何从通用错误类型中获取更详细的错误信息.我将使用的示例来自网络包,特别是DialTimeout函数.

签名是

func DialTimeout(network, address string, timeout time.Duration) (Conn, error)

错误类型仅定义了一个Error() string函数.如果我想找出DialTimeout失败的确切原因,我该如何获取该信息?我发现我可以使用类型断言来获取net.Error特定错误:

con, err := net.DialTimeout("tcp", net.JoinHostPort(address, "22"),
    time.Duration(5) * time.Second)
if err != nil {
    netErr, ok := err.(net.Error)
    if ok && netErr.Timeout() {
        // ...
    } 
} 
Run Code Online (Sandbox Code Playgroud)

但这只能告诉我是否有超时.例如,假设我想区分拒绝连接和没有主机路由.我怎样才能做到这一点?

也许DialTimeout太高级了,不能给我那种细节,但即使看着syscall.Connect,我也看不出如何得到具体的错误.它只是说它返回通用错误类型.将它与Posix connect进行比较,它会让我知道它为什么失败了各种返回码.

我的一般问题是:error如果golang文档没有告诉我可能返回什么类型的错误,我应该如何从泛型类型中提取错误详细信息?

nem*_*emo 15

大多数网络操作返回一个*OpError,其中包含有关错误的详细信息并实现net.Error接口.因此对于大多数用例来说,net.Error 就像您已经使用的那样就足够了.

但对于您的情况,您希望断言返回的错误*net.OpError并使用内部错误:

if err != nil {
    if oerr, ok := err.(*OpError); ok {
        // Do something with oerr.Err
    }
}
Run Code Online (Sandbox Code Playgroud)

一旦你这样做,你就处于平台依赖的境地,因为Linux下的系统调用可能与Windows下的系统调用有所不同.对于Linux,你会做这样的事情:

if oerr.Err == syscall.ECONNREFUSED {
    // Connection was refused :(
}
Run Code Online (Sandbox Code Playgroud)

syscall软件包包含适用于您的平台的重要错误常量.不幸的是golang网站只显示Linux amd64的syscall包.见这里ECONNREFUSED.

找出类型

下次当你想知道某个函数实际返回了什么并且你无法做出正面和反面时,请尝试使用(和朋友)中%#v指定的格式fmt.Printf:

 fmt.Printf("%#v\n", err)
 // &net.OpError{Op:"dial", Net:"tcp", Addr:(*net.TCPAddr)(0xc20006d390), Err:0x6f}
Run Code Online (Sandbox Code Playgroud)

它将打印详细的类型信息,通常非常有用.