Data completamento ricerca: 2007-10-14
HttpWebRequest e connessioni inaffidabili
Il problema
Usando l'oggetto HttpWebRequest con una connessione GPRS, se questa non è particolarmente robusta la richiesta a volte si blocca finchè la connessione GPRS non viene terminata. Il problema è più evidente se la richiesta innesca una connessione GPRS e su alcuni modelli di PocketPC Phone con una ricezione non particolarmente brillante.
Le cause
Per esigenze di sviluppo le cause non sono state approfondite ma sono probabilmente da ricercare nel packet loss. L'oggetto HttpWebRequest si aspetta una determinata quantità di dati e la ricezione di solo una porzione di dati ne provoca il blocco.
La soluzione
La richiesta fortunatamente non è bloccante a livello di sistema operativo, per cui è possibile ovviare in modo abbastanza semplice al problema ricorrendo all'uso dei thread. Non è possibile fare in modo che i dati vengano recuperati tutti, ma è possibile fare in modo che la richiesta non causi un blocco a tempo indefinito e che ritorni invece null.
Esempio
Il codice C# che segue illustra come utilizzare i thread per vincolare il download di un file a un tempo ben definito.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using System.IO;
namespace Updater
{
public class TimedWebRequest
{
#region Constants
public const int DefaultBufferSize = 1024;
public const int DefaultMaxSize = 1048576;
#endregion
#region Private members
private int m_BufferSize = DefaultBufferSize;
private int m_MaxSize = DefaultMaxSize;
private object locker = new object();
#region Private properties. Used to perform locking
private byte[] bRetval = null;
private byte[] ByteRetval
{
get
{
lock (locker)
return bRetval;
}
set
{
lock (locker)
bRetval = value;
}
}
private string sAddress = null;
private string Address
{
get
{
lock (locker)
return sAddress;
}
set
{
lock (locker)
sAddress = value;
}
}
private int iCur = 0;
private int Current
{
get
{
lock (locker)
return iCur;
}
set
{
lock (locker)
iCur = value;
}
}
private int iTot = 0;
private int Total
{
get
{
lock (locker)
return iTot;
}
set
{
lock (locker)
iTot = value;
}
}
private bool bComplete = true;
private bool IsComplete()
{
lock (locker)
return bComplete;
}
#endregion
private void SetComplete(bool bValue)
{
lock (locker)
bComplete = bValue;
}
private void threadFuncB()
{
SetComplete(false);
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(Address);
myRequest.KeepAlive = false;
try
{
byte[] buffer = new byte[BufferSize];
HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse();
Stream stream = myResponse.GetResponseStream();
long len = myResponse.ContentLength;
if (len <= 0)
len = MaxSize;
long total = 0;
List<byte> data = new List<byte>(65536);
Current = 0;
Total = (int)len;
while (total < len)
{
System.Threading.Thread.Sleep(20);
int rbytes = stream.Read(buffer, 0, BufferSize);
for (int i = 0; i < rbytes; i++)
data.Add(buffer[i]);
total += rbytes;
Current = (int)total;
Total = (int)len;
if (total > MaxSize && rbytes == 0)
break;
}
ByteRetval = data.ToArray();
}
catch
{
try
{
myRequest.Abort();
}
catch
{
}
ByteRetval = null;
}
finally
{
SetComplete(true);
}
}
#endregion
#region public members
public delegate void DgtDownloadProgress(int iCurrent, int iTotal);
public event DgtDownloadProgress DownloadProgress;
public int BufferSize
{
get { lock (locker) return m_BufferSize; }
set { lock (locker) m_BufferSize = value; }
}
public int MaxSize
{
get { lock (locker) return m_MaxSize; }
set { lock (locker) m_MaxSize = value; }
}
public string Request(string sURL, int timeout)
{
byte[] result = RequestBinary(sURL, timeout);
if (result != null)
{
MemoryStream ms = new MemoryStream(result);
StreamReader rdr = new StreamReader(ms);
return rdr.ReadToEnd();
}
else
{
return null;
}
}
public byte[] RequestBinary(string sURL, int timeout)
{
int sleeptime = 150;
ByteRetval = null;
Address = sURL;
Thread th = new Thread(new ThreadStart(threadFuncB));
while (IsComplete() == false)
System.Threading.Thread.Sleep(150);
SetComplete(false);
th.Start();
int time = 0;
while (IsComplete() == false)
{
th.Join(sleeptime);
lock (locker)
{
if (Current > Total)
Current = Total;
if (DownloadProgress != null)
DownloadProgress(Current, Total);
}
time += sleeptime;
if (time > timeout)
th.Abort();
}
th = null;
return ByteRetval;
}
public string Request(string sURL)
{
return Request(sURL, 90000);
}
public byte[] RequestBinary(string sURL)
{
return RequestBinary(sURL, 90000);
}
#endregion
}
}