2017-04-19 177 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