Dieser Code funktioniert, Timeouts erhöhen, wenn erwartet (es ist eine modifizierte Version des Beispiels I in der Frage verbunden):
// 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);
}
Das entscheidende Element aus dem Beispiel fehlt fand ich ist Socket.Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds)
. Beachten Sie, dass diese Methode die Liste ändern kann, die an sie übergeben wird (deshalb erstellt mein Code jedes Mal eine neue) und misst die Zeit in Mikrosekunden statt in Millisekunden. Und denken Sie daran, Environment.TickCount
(das ist ein monotonic time source) anstelle von DateTime.Now
für die Zeitmessung verwenden.
Für was sind die Socket.Select Aufrufe gut? Warum nicht einfach sofort senden/empfangen und dann länger schlafen (z. B. 500 ms), wenn eine Ausnahme ausgelöst wird? –
Socket.Select stellt sicher, dass der folgende Vorgang nicht blockiert wird. Wenn es nicht vorhanden wäre, würde das Ganze noch unendlich warten. – skolima
socket.Poll() ist eine etwas einfachere Wahl, wenn Sie nicht auf mehrere Sockets warten müssen. –