2012-11-09 126 views
1

我想写一个小Lwt(和电池)端口扫描器,以更好地 了解Lwt但是,我收到一个奇怪的例外,每当我尝试扫描 太多端口在一次scan_ports_range。我怀疑我的机制 保持连接打开的最大数量是不工作...小Lwt端口扫描器不工作

Exception: Unix.Unix_error (Batteries.Unix.EMFILE, "socket",""). Fatal error: exception Sys_error("/home/(censored)/.opam/4.00.1/lib/utop: Too many open files") (也崩溃UTOP BTW)

代码如下。要触发错误评估: scan_ports_range ~host:"127.0.0.1" (1000,2000)

我的lwt风格的任何批评/建议也是受欢迎的,因为我只有 才开始学习它。

open Lwt 

let addr_parts addr = 
    let (host, port) = String.split addr ":" in (host, int_of_string port) 

let addr ~host ~port = 
    lwt entry = Lwt_unix.gethostbyname host in 
    if Array.length entry.Unix.h_addr_list = 0 then begin 
    failwith (Printf.sprintf "no address found for host %S\n" host) 
    end; 
    return (Unix.ADDR_INET (entry.Unix.h_addr_list.(0), port)) 

let test_connection ?(timeout=1.0) addr = 
    let fd = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in 
    let connect_close = 
    (Lwt_unix.connect fd addr) >>= (fun() -> Lwt_unix.close fd) in 
    try_lwt 
    (pick [connect_close ; Lwt_unix.timeout timeout]) 
    >>= (fun() -> return true) 
    with _ -> return false 

let scan_ports ~host ~ports = 
    ports |> Lwt_list.map_p (fun port -> 
     lwt adr = addr ~host ~port in 
     test_connection adr >>= (fun res -> return (res,port))) 
    >>= (fun l -> return (l |> List.filter_map (function 
     | false, _ -> None | true, port -> Some(port)))) 

let scan_ports_range ?(max_open=20) ~host (a, b) = 
    let rec loop acc enum = 
    match Enum.peek enum with 
    | None -> acc |> List.concat |> List.rev |> return 
    | Some _ -> 
     let ports = enum |> Enum.take max_open |> List.of_enum in 
     let open_ports = scan_ports ~host ~ports in 
     open_ports >>= (fun l -> loop (l::acc) enum) 
    in loop [] (a--b)  

回答

6

由于胡乱猜测,我认为你需要强制插座收盘在超时的情况下,这样的cullprint可能是:

pick [connect_close ; Lwt_unix.timeout timeout] 
+0

你说得对。如果超时,我必须关闭文件描述符。 – rgrinberg

1

下面是根据托马斯更正后的代码。

let test_connection ?(timeout=1.0) addr = 
    let fd = Lwt_unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in 
    let connect_close = 
    (Lwt_unix.connect fd addr) >>= (fun() -> Lwt_unix.close fd) in 
    try_lwt 
    (pick [connect_close ; Lwt_unix.timeout timeout]) 
    >>= (fun() -> return true) 
    with _ -> (Lwt_unix.close fd) >>= (fun() -> return false)