views:

820

answers:

1

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.

+6  A: 

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.

skolima
What are the Socket.Select calls good for? Why not simply Send/Receive immediately and then sleep longer (e.g. 500ms) when an exception is thrown?
Andreas Huber
Socket.Select ensures that the following operation will not block. If it wasn't present, the whole thing would still wait infinitely.
skolima
socket.Poll() is a slightly simpler choice, if you don't need to wait for multiple sockets.
Paul Du Bois