2017-04-19 125 views
1

我在构建以下F#try..with语句的with部分时遇到问题。这里有很多异常处理的例子,但是当主机不存在时,我找不到任何与Dns.GetHostEntry返回的SocketException特别匹配的东西。我会继续寻找,但会欣赏一个指针。如何捕捉SocketException

let test_smtp (smtp_server_address : string) (port : int) = 
    let expectedResultCode = 220 
    let hostEntry : IPHostEntry = 
     try 
      Dns.GetHostEntry(smtp_server_address) 
     with 
      | :? System.Net.Sockets.SocketException -> printfn "GetHostEntry threw an exception " 

    let endPoint : IPEndPoint = new IPEndPoint (hostEntry.AddressList.[0], port) 
    (use tcpSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) 
     tcpSocket.Connect(endPoint) 

     let temp_result = check_response tcpSocket expectedResultCode 
     temp_result 
    ) 

回答

1

到目前为止,您只需添加as (name)语法以给出异常对象的名称,然后使用when (test)语法来测试某个条件是否成立。例如,:

try 
    Dns.GetHostEntry(smtp_server_address) 
with 
    | :? System.Net.Sockets.SocketException as ex when ex.SocketErrorCode = SocketError.HostNotFound -> 
    failwith "Host not found" 

(注:我的原代码,测试SocketErrorCode对一个int,因为我误读了MSDN文档,但SocketErrorCodeSystem.Net.Sockets.SocketError enum value。)有关代码

一个评论:你不需要围绕use tcpSocket = ...表达式的括号。你可以只用代码的其余部分写它内联,而当它超出范围在函数结束F#将处置tcpSocket值:

let test_smtp (smtp_server_address : string) (port : int) = 
    let expectedResultCode = 220 
    let hostEntry : IPHostEntry = 
     try 
      Dns.GetHostEntry(smtp_server_address) 
     with 
      | :? System.Net.Sockets.SocketException as ex when ex.SocketErrorCode = SocketError.HostNotFound -> 
       failwith "GetHostEntry threw an exception " 

    let endPoint : IPEndPoint = new IPEndPoint (hostEntry.AddressList.[0], port) 
    use tcpSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) 
    tcpSocket.Connect(endPoint) 

    check_response tcpSocket expectedResultCode 
+0

修复后,发生了第二个错误。它抱怨11001以及原来的错误说它应该是IPHostEntry。 – octopusgrabbus

+0

@octopusgrabbus - 我原来的回答错了:'SocketErrorCode'是一个枚举。我已经更新了它。让我看看有关IPHostEntry的事情。 – rmunn

+0

...和'printfn'是错误的。 'printfn'返回'()','单元'类型。但是'try ... with'表达式的每个分支需要返回与'Dns.GetHostEntry'相同的类型,否则会抛出异常。所以我更新了我的示例以抛出异常,因为没有有意义的'IPHostEntry'可以返回。除非你重写这个函数来使用[铁路编程](http://fsharpforfunandprofit.com/posts/recipe-part2/)风格,但这是一个更大的问题。 – rmunn

1

下面是不是一个解决方案,但解决方法。它解决了主机未发现错误的问题,而无需使用@rmunn的答案中提到的railway-oriented programming。 (这是一个伟大的文章,顺便说一句。)

所以false可如果Dns.GetHostEntry抛出异常或返回如果check_response未能从HELO正确的响应。

module net_utilF_mod 

open System 
open System.Text 
open System.Threading 
open System.Net 
open System.Net.Sockets 
open System.IO 

type Smtp1() = 
    member this.X = "F#" 

let send_data (socket : Socket) (data : string) = 
    let dataArray : byte [] = Encoding.ASCII.GetBytes(data) 
    socket.Send(dataArray, 0, dataArray.Length, SocketFlags.None) 

let check_response (socket:Socket) expectedResultCode = 
    while socket.Available = 0 do 
     System.Threading.Thread.Sleep 100 

    let responseArray : byte [] = Array.zeroCreate 1024 
    socket.Receive(responseArray, 0, socket.Available, SocketFlags.None) |> ignore 
    let responseData : string = Encoding.ASCII.GetString(responseArray) 
    let responseCode : int = Convert.ToInt32(responseData.Substring(0,3)) 
    if responseCode = expectedResultCode then 
     true 
    else 
     false 

let test_smtp (smtp_server_address : string) (port : int) = 
    let helo_msg = String.Format("HELO {0}\r\n", Dns.GetHostName()) 
    let hostEntry : IPHostEntry = 
     try 
      Dns.GetHostEntry(smtp_server_address) 
     with 
      | :? System.Net.Sockets.SocketException as ex when ex.SocketErrorCode = SocketError.HostNotFound -> 
       let tempEntry : IPHostEntry = Dns.GetHostEntry(@"localhost") 
       tempEntry 

    let email_server_reachable = 
     match hostEntry.HostName with 
     | @"localhost" -> false 
     | _ -> true 


    if false = email_server_reachable then 
     false 
    else 
     let endPoint : IPEndPoint = new IPEndPoint (hostEntry.AddressList.[0], port) 
     (use tcpSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) 
      tcpSocket.Connect(endPoint) |> ignore 

      let temp_response = 
       if true = check_response tcpSocket 220 then 
       send_data tcpSocket helo_msg |> ignore 
       check_response tcpSocket 250    
       else 
       false 

      temp_response 
     ) 
+0

快速的文体评论:我发现'if true =(布尔值)'比简单的'if(布尔值)'更不可读。同样,如果false =(布尔值)'比'if(布尔值)'更不可读。由于Javascript的非严格类型,这是很多Javascript程序员都习惯的习惯,但是在F#中它是不必要的,它有严格的输入,所以你可以使用更易读的if(boolean)。你设置'email_server_reachable'的'match'表达式也可以用'let email_server_reachable = hostEntry.HostName <>“localhost”'更可读。一行,易于阅读。 – rmunn

+0

@rmunn注意到了,谢谢。 – octopusgrabbus