Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to detect a timeout when using asynchronous Socket.BeginReceive?
    primarykey
    data
    text
    <p>Writing an asynchronous Ping using Raw Sockets in F#, to enable parallel requests using as few threads as possible. Not using "System.Net.NetworkInformation.Ping", because it appears to allocate one thread per request. Am also interested in using F# async workflows.</p> <p>The synchronous version below correctly times out when the target host does not exist/respond, but the asynchronous version hangs. Both work when the host does respond. Not sure if this is a .NET issue, or an F# one...</p> <p>Any ideas?</p> <p>(note: the process must run as Admin to allow Raw Socket access)</p> <p>This throws a timeout:</p> <pre><code>let result = Ping.Ping ( IPAddress.Parse( "192.168.33.22" ), 1000 ) </code></pre> <p>However, this hangs:</p> <pre><code>let result = Ping.AsyncPing ( IPAddress.Parse( "192.168.33.22" ), 1000 ) |&gt; Async.RunSynchronously </code></pre> <p>Here's the code...</p> <pre><code>module Ping open System open System.Net open System.Net.Sockets open System.Threading //---- ICMP Packet Classes type IcmpMessage (t : byte) = let mutable m_type = t let mutable m_code = 0uy let mutable m_checksum = 0us member this.Type with get() = m_type member this.Code with get() = m_code member this.Checksum = m_checksum abstract Bytes : byte array default this.Bytes with get() = [| m_type m_code byte(m_checksum) byte(m_checksum &gt;&gt;&gt; 8) |] member this.GetChecksum() = let mutable sum = 0ul let bytes = this.Bytes let mutable i = 0 // Sum up uint16s while i &lt; bytes.Length - 1 do sum &lt;- sum + uint32(BitConverter.ToUInt16( bytes, i )) i &lt;- i + 2 // Add in last byte, if an odd size buffer if i &lt;&gt; bytes.Length then sum &lt;- sum + uint32(bytes.[i]) // Shuffle the bits sum &lt;- (sum &gt;&gt;&gt; 16) + (sum &amp;&amp;&amp; 0xFFFFul) sum &lt;- sum + (sum &gt;&gt;&gt; 16) sum &lt;- ~~~sum uint16(sum) member this.UpdateChecksum() = m_checksum &lt;- this.GetChecksum() type InformationMessage (t : byte) = inherit IcmpMessage(t) let mutable m_identifier = 0us let mutable m_sequenceNumber = 0us member this.Identifier = m_identifier member this.SequenceNumber = m_sequenceNumber override this.Bytes with get() = Array.append (base.Bytes) [| byte(m_identifier) byte(m_identifier &gt;&gt;&gt; 8) byte(m_sequenceNumber) byte(m_sequenceNumber &gt;&gt;&gt; 8) |] type EchoMessage() = inherit InformationMessage( 8uy ) let mutable m_data = Array.create 32 32uy do base.UpdateChecksum() member this.Data with get() = m_data and set(d) = m_data &lt;- d this.UpdateChecksum() override this.Bytes with get() = Array.append (base.Bytes) (this.Data) //---- Synchronous Ping let Ping (host : IPAddress, timeout : int ) = let mutable ep = new IPEndPoint( host, 0 ) let socket = new Socket( AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp ) socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout ) socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout ) let packet = EchoMessage() let mutable buffer = packet.Bytes try if socket.SendTo( buffer, ep ) &lt;= 0 then raise (SocketException()) buffer &lt;- Array.create (buffer.Length + 20) 0uy let mutable epr = ep :&gt; EndPoint if socket.ReceiveFrom( buffer, &amp;epr ) &lt;= 0 then raise (SocketException()) finally socket.Close() buffer //---- Entensions to the F# Async class to allow up to 5 paramters (not just 3) type Async with static member FromBeginEnd(arg1,arg2,arg3,arg4,beginAction,endAction,?cancelAction): Async&lt;'T&gt; = Async.FromBeginEnd((fun (iar,state) -&gt; beginAction(arg1,arg2,arg3,arg4,iar,state)), endAction, ?cancelAction=cancelAction) static member FromBeginEnd(arg1,arg2,arg3,arg4,arg5,beginAction,endAction,?cancelAction): Async&lt;'T&gt; = Async.FromBeginEnd((fun (iar,state) -&gt; beginAction(arg1,arg2,arg3,arg4,arg5,iar,state)), endAction, ?cancelAction=cancelAction) //---- Extensions to the Socket class to provide async SendTo and ReceiveFrom type System.Net.Sockets.Socket with member this.AsyncSendTo( buffer, offset, size, socketFlags, remoteEP ) = Async.FromBeginEnd( buffer, offset, size, socketFlags, remoteEP, this.BeginSendTo, this.EndSendTo ) member this.AsyncReceiveFrom( buffer, offset, size, socketFlags, remoteEP ) = Async.FromBeginEnd( buffer, offset, size, socketFlags, remoteEP, this.BeginReceiveFrom, (fun asyncResult -&gt; this.EndReceiveFrom(asyncResult, remoteEP) ) ) //---- Asynchronous Ping let AsyncPing (host : IPAddress, timeout : int ) = async { let ep = IPEndPoint( host, 0 ) use socket = new Socket( AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp ) socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout ) socket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout ) let packet = EchoMessage() let outbuffer = packet.Bytes try let! result = socket.AsyncSendTo( outbuffer, 0, outbuffer.Length, SocketFlags.None, ep ) if result &lt;= 0 then raise (SocketException()) let epr = ref (ep :&gt; EndPoint) let inbuffer = Array.create (outbuffer.Length + 256) 0uy let! result = socket.AsyncReceiveFrom( inbuffer, 0, inbuffer.Length, SocketFlags.None, epr ) if result &lt;= 0 then raise (SocketException()) return inbuffer finally socket.Close() } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload