Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I achieve non-infinite receive and send timeouts on a socket under Compact Framework?

On full .Net Framework I use the following code:

socket.SetSocketOption(
  SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, readTimeout);
socket.SetSocketOption(
  SocketOptionLevel.Socket, SocketOptionName.SendTimeout, writeTimeout);

However, Windows Mobile does not support this and throws exceptions.

I am currently in the middle of testing this solution for implementing timeouts.

Does anyone know a better way? I'd like to avoid spawning multiple threads if possible, this is an embedded device after all.

like image 895
skolima Avatar asked May 13 '09 15:05

skolima


1 Answers

This code works, raising timeouts when expected (it is a modified version of the example I linked in the question):

// copied from Mono, because CF lacks this enum
enum SocketError
{
    IOPending = 997,
    NoBufferSpaceAvailable = 10055,
    TimedOut = 10060,
    WouldBlock = 10035
}

// milliseconds
int receiveTimeout = 20000;
int sendTimeout = 20000;

public override int Read(byte[] buffer, int offset, int size)
{    
    int startTickCount = Environment.TickCount;
    int received = 0;
    do
    {
        List<Socket> sock = new List<Socket>(new Socket[] {socket});
        Socket.Select(sock, null, null, receiveTimeout*1000 + 1);
        if (Environment.TickCount > startTickCount + receiveTimeout)
            throw new SocketException((int) SocketError.TimedOut);
        try
        {
            received += socket.Receive(buffer, offset + received,
                size - received, SocketFlags.None);
        }
        catch (SocketException ex)
        {
            if (ex.ErrorCode == (int) SocketError.WouldBlock ||
                ex.ErrorCode == (int) SocketError.IOPending ||
                ex.ErrorCode == (int) SocketError.NoBufferSpaceAvailable)
            {
                // socket buffer is probably empty, wait and try again
                Thread.Sleep(30);
            }
            else
                throw; // any serious error occurr
        }
    } while (received < size);
    return received;
}

public override void Write(byte[] buffer, int offset, int size)
{
    int startTickCount = Environment.TickCount;
    int sent = 0;
    do
    {
        List<Socket> sock = new List<Socket>(new Socket[] {socket});
        Socket.Select(null, sock, null, sendTimeout*1000 + 1);
        if (Environment.TickCount > startTickCount + sendTimeout)
            throw new SocketException((int) SocketError.TimedOut);
        try
        {
            sent += socket.Send(buffer, offset + sent,
                size - sent, SocketFlags.None);
        }
        catch (SocketException ex)
        {
            if (ex.ErrorCode == (int) SocketError.WouldBlock ||
                ex.ErrorCode == (int) SocketError.IOPending ||
                ex.ErrorCode == (int) SocketError.NoBufferSpaceAvailable)
            {
                // socket buffer is probably full, wait and try again
                Thread.Sleep(30);
            }
            else
                throw; // any serious error occurr
        }
    } while (sent < size);
}

The crucial element missing from the example I found is Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds). Bear in mind that this method may modify the list that is passed to it (that's why my code creates a new one each time) and measures time in microseconds instead of milliseconds. And remember to use Environment.TickCount (which is a monotonic time source) instead of DateTime.Now for measuring time.

like image 74
skolima Avatar answered Sep 28 '22 07:09

skolima