Edit: The linked "same question" seems to be for regular windows forms apps, not windows RT which has a limited .NET library.
I am developing a Windows App that has the ability to upload a zip file to a server with progress. I am using a custom Stream class to handle the progress updates. The stream class is as follows (snippet):
public class RTMStreamWithProgress : System.IO.Stream
{
private readonly System.IO.Stream file;
private readonly long length;
private long bytesRead;
public event EventHandler<decimal> ProgressChanged;
public RTMStreamWithProgress(System.IO.Stream file)
{
this.file = file;
length = file.Length;
bytesRead = 0;
if (ProgressChanged != null)
{
ProgressChanged(this, 0);
}
}
public override int Read(byte[] buffer, int offset, int count)
{
int result = file.Read(buffer, offset, count);
bytesRead += result;
if (ProgressChanged != null)
{
ProgressChanged(this, (decimal)((double)bytesRead/(double)length));
}
return result;
}
//more stuff
}
And then in my app, I use this to try to upload a file to a server here:
//Initialize updateable stream
RTMStreamWithProgress progStream = new RTMStreamWithProgress(zipAsStream);
progStream.ProgressChanged += upload_ProgressChanged;
private async void upload_ProgressChanged(object sender, decimal e)
{
Debug.WriteLine((e * 100).ToString());
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
HubDataContext globalContext = (HubDataContext) Application.Current.Resources["GlobalDataContext"];
globalContext.SubProgressValue = Convert.ToDouble(e * 100);
});
}
The ProgressBar has a its Value bound to the SubProgressValue property that I am updating. My issue is that I can see in the console via Debug.Writeline that the stream is being read very fast and I get very quick updates multiple times per second, such as:
0.0086728887868017600
0.1474391093756300
2.3676986387968800
4.5879581682181300
6.8082176976393800
//so on and so forth...
But the progress bar is only updating like 2 or 3 times during the entire upload process, and the value it updates to is seemingly random (but still one of the values that was reported by upload_ProgressChanged. The reason I have the binding update inside of the this.Dispatcher... is because it wouldn't work without that, causing crashes relating to trying to access the UI from a separate thread.
How can I get the progress bar to update just as fast and as often as I can seeing with Debug.WriteLine, while keeping the Async-Await design?
Related
I'm creating a UWP program for Raspberry Pi. One of the functions of the program is to send and receive some data from an Arduino.
The problem is when I try sending data to the Arduino rapidly and many times, I end up with System.Runtime.InteropServices.COMException The operation identifier is not valid. originating from DataWriter.DetachStream().
Sending the data rapidly works just fine, up until a certain amount it seems, where I get the exception thrown.
With "rapid", I mean using an auto clicker to click a button to send data each millisecond.
I've not tried sending data slowly many times in a row to reproduce the issue, as this would probably take a long time (seeing it takes about 10-20 seconds with 1ms delay between transmissions.
I've been searching for a solution to this problem for way too many hours, but I can't seem to find any related questions/solutions.
public sealed partial class LightControl : Page
{
int Alpha;
int Red;
int Green;
int Blue;
// This is the handler for the button to send data
private void LightButton_Click(object sender, RoutedEventArgs e)
{
if (!(sender is Button button) || button.Tag == null) return;
string tag = button.Tag.ToString();
Alpha = int.Parse(tag.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
Red = int.Parse(tag.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
Green = int.Parse(tag.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
Blue = int.Parse(tag.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
SendLightData();
}
public async void SendLightData()
{
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
}
public class ArduinoHandler
{
// Code for singleton behaviour. Included for completeness
#region Singleton behaviour
private static ArduinoHandler arduinoHandler;
private static Object singletonCreationLock = new Object();
public static ArduinoHandler Current
{
get
{
if (arduinoHandler == null)
{
lock (singletonCreationLock)
{
if (arduinoHandler == null)
{
CreateNewArduinoHandler();
}
}
}
return arduinoHandler;
}
}
public static void CreateNewArduinoHandler()
{
arduinoHandler = new ArduinoHandler();
}
#endregion
private DataWriter dataWriter;
private Object WriteCancelLock = new Object();
public async Task WriteAsync(DataEnum receiver, DataEnum sender,
byte commandByte1, byte dataByte1, byte dataByte2, byte dataByte3,
byte dataByte4, CancellationToken cancellationToken)
{
try
{
dataWriter = new DataWriter(arduinos[receiver].OutputStream);
byte[] buffer;
Task<uint> storeAsyncTask;
lock (WriteCancelLock)
{
buffer = new byte[8];
buffer[0] = Convert.ToByte(receiver);
buffer[1] = Convert.ToByte(sender);
buffer[2] = commandByte1;
buffer[3] = dataByte1;
buffer[4] = dataByte2;
buffer[5] = dataByte3;
buffer[6] = dataByte4;
buffer[7] = Convert.ToByte('\n');
cancellationToken.ThrowIfCancellationRequested();
dataWriter.WriteBytes(buffer);
storeAsyncTask = dataWriter.StoreAsync().AsTask(cancellationToken);
}
uint bytesWritten = await storeAsyncTask;
Debug.Write("\nSent: " + BitConverter.ToString(buffer) + "\n");
}
catch (Exception e)
{
Debug.Write(e.Message);
}
finally
{
dataWriter.DetachStream(); // <--- I've located the exception to originate from here, using the debugger in Visual Studio
dataWriter.Dispose();
}
}
public enum DataEnum
{
Light = 0x01,
Piston = 0x02,
PC = 0x03,
LightArduino = 0x04
}
}
I would expect the Raspberry Pi to send the data to the Arduino, but after a while with rapid data transmission, the exception is thrown.
Update
I tried using a local variable for the dataWriter as suggested below, but this causes strange behavior after a while with rapid data transmission. Just as if it slows down. It is worth noting that I don't get an exception anymore.
Quite hard trying to explain how it behaves, but the Debug.Write logs the message I'm sending (which works fine). However, after a while, it seems to "slow down", and even after I stop clicking, the data is being sent once every second or so. It works completely fine up until this point. So I'm wondering if there is a limit of some sort I'm hitting?
Update 2
I seem to have found a rather "hacky" and weird solution to the problem.
If I use Serial.write() on the Arduino to send the data back to the Raspberry Pi, it seems to have fixed the issue somehow.
If anyone knows how this worked, I'd be very interested to know :)
const int payloadSize = 8;
byte payload[payloadSize]
int numBytes;
// Called each time serial data is available
void serialEvent()
{
numBytes = Serial.available();
if (numBytes == payloadSize)
{
for (int i = 0; i < payloadSize; i++)
{
payload[i] = Serial.read();
Serial.write(payload[i]); // <--- This line fixed the issue for whatever reason
}
}
checkData(); // Function to do something with the data
for (int i = 0; i < payloadSize; i++)
{
payload[i] = None;
}
numBytes = 0;
}
Your problem originates from the fact that you are using a fire-and-forget approach of working with async method. When you call SendLightData() in quick succession, it doesn't wait for the previous WriteAsync operation to complete.
Once the execution reaches the first actual await expression - which is the await storeAsyncTask line, the UI thread is freed up to handle another button click.
This new button click can start executing and overwrite the dataWriter field in the same instance of ArduinoHandler. When the first storeAsyncTask finishes executing, it will actually datach the dataWriter of the second call, not its own. This can lead to multiple different sorts of issues and race conditions.
So you must make sure that it is not possible to click the button before the previous operation actually executes. You could use a boolean flag for that as a simple solution.
private bool _isWorking = false;
public async void SendLightData()
{
if (!_isWorking)
{
try
{
_isWorking = true;
await ArduinoHandler.Current.WriteAsync(ArduinoHandler.DataEnum.LightArduino,
ArduinoHandler.DataEnum.Light, Convert.ToByte(LightConstants.LightCommand.LightCommand),
Convert.ToByte(Red), Convert.ToByte(Green), Convert.ToByte(Blue), Convert.ToByte(Alpha),
WriteCancellationTokenSource.Token);
}
finally
{
_isWorking = false;
}
}
This will ensure that two operations never execute simultaneously.
Other solution could be to not store the data writer as a field and just have it as a local variable. When you avoid all shared state between the calls, you can safely know that there will be no race condition stemming from overwriting.
I have a server software that has a single listening socket that then spans off multiple sockets (10 -30) which I then stream data to.
If I startup my application it used about 2-3% cpu usage on my 8 vCPU VM. After some time, generally 1-2 weeks the application suddenly starts using 60-70% cpu usage, and the thread count seems to stay stable, it does not increase.
I have run my C# profiler on my code and it comes down to the following line of code System.net.Socket.beginReceive().
I am using .net async sockets. below is my ReceiveCallBack My suspicion is that I am not handling the case when bytesRead is NOT >0. How should I modify my function below to handle that case correctly?
public static void ReadCallback(IAsyncResult ar)
{
SocketState tmpRef = null;
try
{
if (ar != null)
{
tmpRef = (SocketState)ar.AsyncState;
if (tmpRef != null)
{
// Read data from the client socket.
int bytesRead = tmpRef.WorkSocket.Client.EndReceive(ar);
//Start Reading Again
tmpRef.BeginReading(tmpRef._receievCallbackAction);
if (bytesRead > 0)
{
// Check if we have a complete message yet
for (var i = 0; i < bytesRead; i++)
{
if (tmpRef._receiveBuffer[i] == 160)
{
var tmpBuffer = new byte[i];
Array.Copy(tmpRef._receiveBuffer, tmpBuffer, i);
//Execute callback
tmpRef._receievCallbackAction(tmpBuffer);
break;
}
}
}
}
}
}
catch (Exception e)
{
if (tmpRef != null)
{
//Call callback with null value to indicate a failier
tmpRef._receievCallbackAction(null);
}
}
}
Full code: (Sorry don't want to dirty the post)
https://www.dropbox.com/s/yqjtz0r3ppgd11f/SocketState.cs?dl=0
The problem is if you do not have enough bytes yet your code spins forever waiting for the next byte to show up.
What you need to do is make a messageBuffer that survive between calls and write to that till you have all the data you need. Also, by the way your code looks you look have the opportunity to overwrite tmpRef._receiveBuffer before you have copied all the data out, your BeginReading needs to start after the copy if you are sharing a buffer.
public class SocketState
{
private readonly List<byte> _messageBuffer = new List<byte>(BufferSize);
//...
/// <summary>
/// Async Receive Callback
/// </summary>
/// <param name="ar"></param>
public static void ReadCallback(IAsyncResult ar)
{
SocketState tmpRef = null;
try
{
if (ar != null)
{
tmpRef = (SocketState)ar.AsyncState;
if (tmpRef != null)
{
// Read data from the client socket.
int bytesRead = tmpRef.WorkSocket.Client.EndReceive(ar);
if (bytesRead > 0)
{
//Loop over the bytes we received this read
for (var i = 0; i < bytesRead; i++)
{
//Copy the bytes from the receive buffer to the message buffer.
tmpRef._messageBuffer.Add(tmpRef._receiveBuffer[i]);
// Check if we have a complete message yet
if (tmpRef._receiveBuffer[i] == 160)
{
//Copy the bytes to a tmpBuffer to be passed on to the callback.
var tmpBuffer = tmpRef._messageBuffer.ToArray();
//Execute callback
tmpRef._receievCallbackAction(tmpBuffer);
//reset the message buffer and keep reading the current bytes read
tmpRef._messageBuffer.Clear();
}
}
//Start Reading Again
tmpRef.BeginReading(tmpRef._receievCallbackAction);
}
}
}
}
catch (Exception e)
{
if (tmpRef != null)
{
//Call callback with null value to indicate a failier
tmpRef._receievCallbackAction(null);
}
}
}
//...
You are explaining that the problems occurs after 1-2 weeks, which is quite rare then.
I would suggest you to orientate your researchs by improving the exception handling in your readcallback.
Within this exception handling it turns out that you are invoking the callbackAction with null.
Maybe you should consider answering the following questions :
How does the callbackAction behaves when invoked with null tmpRef._receievCallbackAction(null);
What kind of exception is caught? If it is a SocketException, maybe look at the ErrorCode, which might give you an indication
Would it be possible to dump the stack trace to know exactly where it fails ?
Some other weak point : the begin receive uses this as state object.
WorkSocket.Client.BeginReceive(_receiveBuffer, 0, BufferSize, 0, ReadCallback, this);
So it means that the thread safeness of the readcallback is not entirely guaranteed, because the call to BeginReading will occurs while you didn't process the _receiveBufferyet.
I am developing a Windows application that uploads a file to a webserver, IIS. My code is working just fine when I run the app on a 64Bit Machine. Upload to the IIS is working. But when I run it on a 32Bit machine, the upload is not working.
I think it has something to do with IIS. But I donĀ“t know what it could be. Does someone has experienced same issues?
UPDATE: This has nothing to with the server side. I tested several endpoints, but nothing worked.
This must be related to my upload code. This code is working from 64Bit Apps but not on 32Bit:
try
{
System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient();
hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml");
hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate");
hc.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0");
hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1");
using (VirtualStream ms = new VirtualStream() { Size = UploadSize })
{
StreamContent content = new StreamContent(ms, BufferSize);
// time for the calculation of the total average throughput
var overallStart = DateTime.Now;
var start = DateTime.Now;
var responseTask = hc.PostAsync(URL, content);
while (!responseTask.IsCompleted)
{
// Check the Exit and Abort Constraints
if ((DateTime.Now - overallStart).TotalMilliseconds > MaxTestLength || _cancelationRequested)
{
System.Diagnostics.Debug.WriteLine("Bytes sent " + bytesSent);
hc.CancelPendingRequests();
IsRunning = false;
return;
}
try
{
bytesSent = ms.Position - bytesOfCalibrationPhase;
}
catch (Exception)
{
// The Upload is an async process which dispses the underlying stream when the upload finishes
// In some cases this could lead to ObjectDiposed Exceptions when accessing the current stream position
// If it is the case, the upload has finished....
break;
}
}
BytesSent is always "0" on 32Bit machines...Why is that?
It seems to me that the loop you have is there for three reasons:
1) You want to cancel the upload if cancellation is requested
2) You want to cancel the upload if there is a timeout
3) you want to know the progress of the upload
I suggest that you remove the loop completely, and achieve these tree goals in a different way. Here is how you can do this:
For (1), use the other overload of the PostAsync method that has a CancellationToken parameter. This allows you to provide a token that you can use from somewhere else to cancel the upload operation.
For (2), you can use the CancellationTokenSource (that you used to create the CancellationToken), to request that the upload operation be canceled after some time (if the task was not already completed). See the CancelAfter method.
Here is some code sample for (1) and (2):
Put the following two lines in some place (probably as fields) so that these variables are available from both your upload code and the code that might wish to cancel the upload:
CancellationTokenSource cancellation_token_source = new CancellationTokenSource();
CancellationToken cancellation_token = cancellation_token_source.Token;
The following line of code will setup automatic cancellation after 10 seconds:
cancellation_token_source.CancelAfter(TimeSpan.FromSeconds(10));
In the following line, we pass the cancellation_token to the PostAsync method:
var responseTask = hc.PostAsync(URL, content, cancellation_token);
I noticed you were waiting for responseTask.IsCompleted to become true. This means that you don't want your method to return until the upload is complete. In this case, use the following to wait for the upload to complete.
responseTask.Wait();
In case you want to convert your method to become asynchronous, mark your method as async, and instead use the following:
await responseTask;
You can use the following to cancel the upload:
cancellation_token_source.Cancel();
For (3), first look at the answers in this question.
If this does not work for you, I have the following suggestion:
You can create a decorator for the Stream class, that informs you when the stream was read, like this (please note the Read method):
public class ReadNotifierStreamWrapper : Stream
{
private readonly Stream m_Stream;
private readonly Action<int> m_ReadNotifier;
public ReadNotifierStreamWrapper(Stream stream, Action<int> read_notifier)
{
m_Stream = stream;
m_ReadNotifier = read_notifier;
}
public override void Flush()
{
m_Stream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
return m_Stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
m_Stream.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count)
{
var bytes_read = m_Stream.Read(buffer, offset, count);
m_ReadNotifier(bytes_read);
return bytes_read;
}
public override void Write(byte[] buffer, int offset, int count)
{
m_Stream.Write(buffer, offset, count);
}
public override bool CanRead
{
get { return m_Stream.CanRead; }
}
public override bool CanSeek
{
get { return m_Stream.CanSeek; }
}
public override bool CanWrite
{
get { return m_Stream.CanWrite; }
}
public override long Length
{
get { return m_Stream.Length; }
}
public override long Position
{
get { return m_Stream.Position; }
set { m_Stream.Position = value; }
}
}
And then you can use it to wrap your ms stream like this:
int total_bytes = 0;
var stream_wrapper = new ReadNotifierStreamWrapper(ms , bytes =>
{
total_bytes += bytes;
Debug.WriteLine("Bytes sent " + total_bytes);
});
HttpContent content = new StreamContent(stream_wrapper); //Here we are creating the StreamContent from stream_wrapper instead of ms
This way you will get a notification when the stream is being read. And you will know how much bytes were read.
Please note that you might need to work more on the ReadNotifierStreamWrapper class to make it better. For example, maybe the HttpClient decides for some reason that it wants to seek the stream via the Seek method. You might need to take that into account. Although, I don't think that HttpClient will do this since it needs to read the whole file to upload it all, it does not make sense to skip parts of the file. You can put some breakpoints on the Seek method of ReadNotifierStreamWrapper to see if it will get called.
The exception you get;
EventSourceException: No Free Buffers available from the operating
system
is caused by the Debug.WriteLine, which uses ETW (Event Tracing for Windows). ETW is a Windows kernel-level tracing facility that relies on a number of buffers to cache data before writing it to disk. ETW uses the buffer size and the size of physical memory to calculate the maximum number of buffers allocated for the event tracing session's buffer pool. So, if your application is 64-bit ETW will allocate more buffers due to there being more addressable memory available.
The reason you are hitting the limit is that you are writing debugging events faster than ETW can process the buffers.
I believe it is possible to increase the maximum number of buffers using registry entries, but this will increase memory consumption and you have the potential to hit the limit again with bigger files, slower connection, etc.
So your best bet is to either sleep the thread on each loop to write tracing events at a lower rate or replace the Debug.WriteLine with some other form of logging.
I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will receive more than 2000 measurements (each measurement is 7 bytes, all in hex). And they are firing at the same time. Right now I am polling from 4 serial ports. Also, I translate each measurement into String and append it to a Stringbuilder. When I finish receiving data, they will be ouput in to a file. The problem is the CPU consumption is very high, ranging from 80% to 100%.
I went though some articles and put Thread.Sleep(100) at the end. It reduces CPU time when there is no data coming. I also put Thread.Sleep at the end of each polling when the BytesToRead is smaller than 100. It only helps to a certain extent.
Can someone suggest a solution to poll from serial port and handle data that I get? Maybe appending every time I get something causes the problem?
//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox, StringBuilder data)
{
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));
int bytesRead;
int t;
Byte[] dataIn;
while (mySerialPort.IsOpen)
{
try
{
if (mySerialPort.BytesToRead != 0)
{
//trying to read a fix number of bytes
bytesRead = 0;
t = 0;
dataIn = new Byte[bytesPerMeasurement];
t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
bytesRead += t;
while (bytesRead != bytesPerMeasurement)
{
t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
bytesRead += t;
}
//convert them into hex string
StringBuilder s = new StringBuilder();
foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
var line = s.ToString();
var lineString = string.Format("{0} ---- {2}",
line,
mySerialPort.BytesToRead);
data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.
////use delegate to change UI thread...
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));
if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
}
else{Thread.Sleep(100);}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
}
}
this is not a good way to do it, it far better to work on the DataReceived event.
basically with serial ports there's a 3 stage process that works well.
Receiving the Data from the serial port
Waiting till you have a relevant chunk of data
Interpreting the data
so something like
class DataCollector
{
private readonly Action<List<byte>> _processMeasurement;
private readonly string _port;
private SerialPort _serialPort;
private const int SizeOfMeasurement = 4;
List<byte> Data = new List<byte>();
public DataCollector(string port, Action<List<byte>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort = new SerialPort(port);
_serialPort.DataReceived +=SerialPortDataReceived;
}
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while(_serialPort.BytesToRead > 0)
{
var count = _serialPort.BytesToRead;
var bytes = new byte[count];
_serialPort.Read(bytes, 0, count);
AddBytes(bytes);
}
}
private void AddBytes(byte[] bytes)
{
Data.AddRange(bytes);
while(Data.Count > SizeOfMeasurement)
{
var measurementData = Data.GetRange(0, SizeOfMeasurement);
Data.RemoveRange(0, SizeOfMeasurement);
if (_processMeasurement != null) _processMeasurement(measurementData);
}
}
}
Note: Add Bytes keeps collecting data till you have enough to count as a measurement, or if you get a burst of data, splits it up into seperate measurements.... so you can get 1 byte one time, 2 the next, and 1 more the next, and it will then take that an turn it into a measurement. Most of the time if your micro sends it in a burst, it will come in as one, but sometimes it will get split into 2.
then somewhere you can do
var collector = new DataCollector("COM1", ProcessMeasurement);
and
private void ProcessMeasurement(List<byte> bytes)
{
// this will get called for every measurement, so then
// put stuff into a text box.... or do whatever
}
First of all consider reading Using Stopwatches and Timers in .NET. You can break down any performance issue with this and tell exactly which part of Your code is causing the problem.
Use SerialPort.DataReceived Event to trigger data receiving process.
Separate receiving process and data manipulation process. Store Your data first then process.
Do not edit UI from reading loop.
I guess what you should be doing is adding an event handler to process incoming data:
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
This eliminates the need to run a separate thread for each serial port you listen to. Also, each DataReceived handler will be called precisely when there is data available and will consume only as much CPU time as is necessary to process the data, then yield to the application/OS.
If that doesn't solve the CPU usage problem, it means you're doing too much processing. But unless you've got some very fast serial ports I can't imagine the code you've got there will pose a problem.
I'm having a hard time getting something done.
I am building an mailclient using asp.net MVC 4.
I am at the point that i have to download an image related to a message ( NOT an attachment ) to the client browser.
Now i have this setup :
Client browser -> Controller/backend -> Mail server
to clarify : i have a client request containing the content ID of the image, the right mailbox, message etc. With that information i can download the image from the Mail server and upload it to the client.
Now comes the hard part: I want to do this asynchronous. I want to be able to download a chunk of 512 KB from the mailserver, decode that part, and send it to the client.. fetch - decode - send.. So long that the browser got all the data of the image.
I just dont want to first download ALL the data first to the server and then create a new memorystream with all that data and return that as a fileresult. I am just too afraid of getting too large files in my memory and block other processes etc.
I am planning to use this method too of uploading real attachments ( which could be 100's of MBs ). So i am gonna need that method later on.
Now i just have no idea how to achieve this, because i have a connection to the mail server, and i have a connection to the client. and i have to pass data to a new stream or something to get this done..
Can someone please help me?
Edit: to clarify: no i cannot refer to the file on the mail server. i HAVE to download the file to the server through sockets.
Edit2: could http chuncked be the solution? If yes, could you give me a small example?
You just need to copy data from one stream (the tcp connection to the mail server) to another (the http connection to the browser), right? If you want to scale, you'll need to use non-blocking IO as described in this article. So you'll want to call the code in that article from an IHttpAsyncHandler implementation. You'll end up with something like this:
class MyHandler : IHttpAsyncHandler
{
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
Stream src = null; // remote data source
Stream dst = context.Response.OutputStream;
// set content type, etc
var res = new MyResult();
AsynchCopy(src, dst, () =>
{
((ManualResetEvent)res.AsyncWaitHandle).Set();
cb(res);
src.Close();
dst.Flush();
});
return res;
}
public void EndProcessRequest(IAsyncResult result)
{
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
class MyResult : IAsyncResult
{
public MyResult()
{
AsyncWaitHandle = new ManualResetEvent(false);
}
public object AsyncState
{
get { return null; }
}
public WaitHandle AsyncWaitHandle
{
get;
private set;
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return AsyncWaitHandle.WaitOne(0); }
}
}
public static void AsynchCopy(Stream src, Stream dst, Action done)
{
byte[] buffer = new byte[2560];
AsyncCallback readCallback = null, writeCallback = null;
readCallback = (readResult) =>
{
int read = src.EndRead(readResult);
if (read > 0)
{
dst.BeginWrite(buffer, 0, read, writeCallback, null);
}
else
{
done();
}
};
writeCallback = (writeResult) =>
{
dst.EndWrite(writeResult);
src.BeginRead(buffer, 0, buffer.Length, readCallback, null);
};
src.BeginRead(buffer, 0, buffer.Length, readCallback, null);
}
}
The above code is untested and doesn't contain error handling, but it should get you started.