I am using a Webclient with a CookieContainer to download data from a webservice (Sharepoint).
I want to DownloadDataAsync so that I download multiple documents and update a single Progress Bar for each Document as it downloads. The non async version - DownloadData does not send Progress updates.
How do I get the Async version to wait at the doc.BinaryData = xx line before moving on the next Document?
How do I get the byte array from the DownloadFileCompleted event?
How can apply the changes to the progressbar without using DoEvents?
partial class Form()
{
void Main()
{
List urls= new List();
//urls.Add("xxxxxxx"); //get them from somewhere
for (int i = 0; i < urls.Count; i++)
{
var doc = new Document();
doc.BinaryData = DocumentAsArray(urls.ElementAt(i));
entities.AddToDocument(doc);
}
}
public byte[] DocumentAsArray(string URL)
{
byte[] #return = null;
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
client.CookieContainer = spAuthentication.CookieContainer;
// Assign the events to capture the progress percentage
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadDataAsync(new Uri(URL));
}
return #return;
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBarControlFile.Position = e.ProgressPercentage;
var newPc = string.Format("Downloaded {0} %", e.ProgressPercentage);
labelControlFile.Text = newPc;
Application.DoEvents();
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
progressBarControlFile.Position = progressBarControlFile.Properties.Maximum;
labelControlFile.Text = string.Format("{0} %", progressBarControlFile.Properties.Maximum);
Application.DoEvents();
}
}
public class CookieAwareWebClient : WebClient
{
public CookieContainer CookieContainer { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
var webRequest = request as HttpWebRequest;
if (webRequest != null)
{
webRequest.CookieContainer = this.CookieContainer;
webRequest.KeepAlive = false;
}
return request;
}
}
}
You have to wait for the DownloadFileCompleted event. You can either set a volatile bool on which you poll or do this with an event. There are only minor differences performance-wise, but generally probably events are the cleaner solution (I prefer bool-polling). You might in your case actually want to wait for this to happen at the end of DocumentAsArray.
http://msdn.microsoft.com/en-us/library/ms144190.aspx The downloaded data is available in the Result property.
I don't know the DoEvents methods - if I understand correctly what you want to do, you want to update your UI? You would have to call InvokeEx (at least that is the easiest and.. only way I know). Look at this great :) article http://www.andreas-reiff.de/2011/06/accessing-a-winformwpf-control-from-another-thread-through-invoke/ . Alternatively, stay at stackoverflow and look at Best Way to Invoke Any Cross-Threaded Code?.
So to recap, if you just want the async to get the progress-update, simply change your DocumentAsArray function to wait for the DownloadFileCompleted event as outlined in 1. Then all you have to do is to be careful that you do not call your UI code from another thread - you should get an exception in Debug telling you not to do so (it runs fine in Release - most of the time). So use the InvokeEx-call.
Related
I have a MQTT calls inside a loop and in each iteration, it should return a response from the subscriber so that I could use the value being forwarded after I published. But the problem is I don't know how would I do it.
I hope you have an idea there or maybe if I'm just not implementing it right, may you guide me through this. Thanks.
Here's my code:
// MyClientMgr
class MyClientMgr{
public long CurrentOutput { get; set; }
public void GetCurrentOutput(MyObjectParameters parameters, MqttClient client)
{
MyMessageObject msg = new MyMessageObject
{
Action = MyEnum.GetOutput,
Data = JsonConvert.SerializeObject(parameters)
}
mq_GetCurrentOutput(msg, client);
}
private void mq_GetCurrentOutput(MyMessageObject msg, MqttClient client)
{
string msgStr = JsonConvert.SerializeObject(msg);
client.Publish("getOutput", Encoding.UTF8.GetBytes(msgStr),
MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
client.MqttMsgPublishReceived += (sender, e) =>{
MyObjectOutput output = JsonConvert.DeserializeObject<MyObjectOutput>(Encoding.UTF8.GetString(e.Message));
CurrentOutput = output;
};
}
}
// MyServerMgr
class MyServerMgr
{
public void InitSubscriptions()
{
mq_GetOutput();
}
private void mq_GetOutput()
{
MqttClient clientSubscribe = new MqttClient(host);
string clientId = Guid.NewGuid().ToString();
clientSubscribe.Connect(clientId);
clientSubscribe.Subscribe(new string[] { "getOutput" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
MqttClient clientPublish = new MqttClient(host);
string clientIdPub = Guid.NewGuid().ToString();
clientPublish.Connect(clientIdPub);
clientSubscribe.MqttMsgPublishReceived += (sender, e) => {
MyMessageObj msg = JsonConvert.DeserializeObject<MyMessageObj>(Encoding.UTF8.GetString(e.Message));
var output = msg.Output;
clientPublish.Publish("getOutput", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(output)), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}
}
}
// MyCallerClass
class MyCallerClass
{
var host = "test.mqtt.org";
var myServer = new MyServerMgr(host);
var myClient = new MyClientMgr();
myServer.InitSubscriptions();
MqttClient client = new MqttClient(host);
for(int i = 0; i < 10; i++)
{
long output = 0;
MyObjectParameters parameters = {};
myClient.GetCurrentOutput(parameters, client) // here I call the method from my client manager
// to publish the getting of the output and assigned
// below for use, but the problem is the value doesn't
// being passed to the output variable because it is not
// yet returned by the server.
// Is there a way I could wait the process to
// get the response before assigning the output?
output = myClient.CurrentOutput; // output here will always be null
// because the response is not yet forwarded by the server
}
}
I have a loop in my caller class to call the mqtt publish for getting the output, but I have no idea how to get the output before it was assigned, I want to wait for the response first before going to the next.
I've already tried doing a while loop inside like this:
while(output == 0)
{
output = myClient.CurrentOutput;
}
Yes, I can get the output here, but it will slow down the process that much. And sometimes it will fail.
Please help me. Thanks.
It looks like you are trying to do synchronous communication over an asynchronous protocol (MQTT).
By this I mean you want to send a message and then wait for a response, this is not how MQTT works as there is no concept of a reply to a message at the protocol level.
I'm not that familiar with C# so I'll just give an abstract description of possible solution.
My suggestion would be to use a publishing thread, wait/pulse (Look at the Monitor class) to have this block after each publish and have the message handler call pulse when it has received the response.
If the response doesn't contain a wait to identify the original request you will also need a state machine variable to record which request is in progress.
You may want to look at putting a time out on the wait in case the other end does not respond for some reasons.
You can use AutoResetEvent class that has WaitOne() and Set() methods. Using WaitOne() after publish will wait until the message is published and using Set() under client_MqttMsgPublishReceived event will release the wait when the subscriber received the message he subscribed for.
I am using a background worker for a time consuming operation ( To upload some data to server and download some data from server). All server communications are via http requests (GET,POST,PUT,Delete) and the code i used are
private void btnSync_Click(object sender, RoutedEventArgs e)
{
btnSync.IsEnabled = false;
BackgroundWorker syncWorker = new BackgroundWorker();
syncWorker.WorkerReportsProgress = true;
syncWorker.WorkerSupportsCancellation = true;
syncWorker.DoWork+=new DoWorkEventHandler(syncWorker_DoWork);
syncWorker.ProgressChanged+=new ProgressChangedEventHandler(syncWorker_ProgressChanged);
syncWorker.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(syncWorker_RunWorkerCompleted);
syncWorker.RunWorkerAsync();
}
private void syncWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker target = (BackgroundWorker)sender;
foreach (...........) // a loop to do some set of actions on some objects
{
target.ReportProgress(1, obj);
target.ReportProgress(2, obj);
target.ReportProgress(3, obj);
}
target.ReportProgress(4);
}
private void syncWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id); //Issue a GET request and read and process resposne (deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 2)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne(deserialize json data and insert to local DB)
}
if (e.ProgressPercentage == 3)
{
string id= e.UserState.ToString();
mySyncHelper.DOGETFROMServer(id);//Issue a GET request and read and process resposne (deserialize json data and insert/Update response data to local DB)
}
if (e.ProgressPercentage ==4)
{
mySyncHelper.DOPOSTToServer();//Issue a POST request and read and process resposne (deserialize json data and insert/Update response data to local DB)
mySyncHelper.DOPUTToServer();
mySyncHelper.DODELETEToServer();
}
}
private void syncWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
txtSyncStatus.Visibility = Visibility.Visible;
txtSyncStatus.Text = string.Concat(DbookLanguage.message_error, Environment.NewLine , e.Error.Message.ToString());
}
else
{
btnSync.IsEnabled = true;
txtSyncStatus.Text = "";
}
}
The problem i am facing is # some stages the application is waiting some time for getting response back for the http requests and during this state the entire application goes to freezed state . The code i used for getting response from server is
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
and on this state the application is non responding .
How can i prevent this ? Is any other good way to make series of Http requests and make app UI interactive
As suggested in comments, the code in "ProgressChanged" runs on UI thread which is causing your UI to freeze (because you have server request in this handler).
ProgressChanged
handler is useful (and should be used) only when you have some regular UI updates (like percentage completed). In your cases, consider moving your entire code to a new method (say GetFromServer(int id)) and make call to this method from inside DoWork. This will cause all this I/O work running on background thread.
C#, .Net 4.5. I have a queue that contains objects which are to be processed. Processing includes obtaining data with the URL which specified in one of the fields of the object. In the course of operation the new objects can be added to the queue. When I tried to do the work with the network asynchronous, I faced with a problem.
Here is a minimum code.
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
string[] urls = { "http://www.stackoverflow.com/",
"http://www.google.com/",
"http://www.microsoft.com/" };
int i = 0;
Queue<MyClass1> queue = new Queue<MyClass1>();
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(urls[i]);
webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
queue.Enqueue(new MyClass1(urls[i], webRequest.GetResponseAsync()));
while (queue.Count > 0)
{
MyClass1 o = queue.Dequeue();
o.RespTask.Wait();
Debug.Print("Url: {0}, bytes: {1}", o.Url,
o.RespTask.Result.ContentLength);
i++;
if (i < urls.Length)
{
webRequest = (HttpWebRequest)WebRequest.Create(urls[i]);
webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;
queue.Enqueue(new MyClass1(urls[i], webRequest.GetResponseAsync()));
}
}
}
}
public class MyClass1
{
public MyClass1() { }
public MyClass1(string url, Task<WebResponse> respTask)
{
Url = url;
RespTask = respTask;
}
public string Url;
public Task<WebResponse> RespTask;
}
That code hangs on o.RespTask.Wait(); on third iteration of cycle. Before this call o.RespTask.Status has value WaitingForActivation and waiting lasts forever. What am I doing wrong?
UPDATE. I checked the code on 3 boxes. On two of them (Win7 32-bit and Win7 64-bit) the program hangs. On the third (Win7 64-bit) everything is working fine. It seems to me very strange.
This code has ceased to hang after such a modification:
...
Debug.Print("Url: {0}, bytes: {1}", o.Url,
o.RespTask.Result.ContentLength);
o.RespTask.Result.Close();
i++;
...
My mistake was that I did not pay attention to the fact that the call to the Close method of the HttpWebResponse class is a must.
I need to send HTTP requests with all the standard RESTful methods and access to the body of the request in order to send/receive JSON with it. I've looked into,
WebRequest.HttpWebRequest
This works almost perfectly, but there are cases where, for example, if the server is down the function GetResponse can take several seconds to return- since it is a synchronous method- freezing the application for that period. The asynchronous version of this method, BeginGetResponse, does not seem to work asynchronously (in Unity anyway) as it still freezes the application for that period.
UnityEngine.WWW#
Only supports POST and GET requests for some reason- but I also need PUT and DELETE (standard RESTful methods) so I didn't bother looking into it any further.
System.Threading
In order to run WebRequest.HttpWebRequest.GetResponse without freezing the application I looked into using threads. Threads seem to work in the editor (but seem extremely volatile- if you don't stop a thread when the application exits it keeps running in the editor forever even when you stop it), and when built to an iOS device crash it as soon as I try to start a thread (I forgot to write down the error and I don't have access to it right now).
Run threads in a native iOS app with a bridge to the Unity app
Ridiculous, not even going to attempt this.
UniWeb
This. I would like to know how they managed it.
Here is an example of the WebRequest.BeginGetResponse method I am trying,
// The RequestState class passes data across async calls.
public class RequestState
{
const int BufferSize = 1024;
public StringBuilder RequestData;
public byte[] BufferRead;
public WebRequest Request;
public Stream ResponseStream;
// Create Decoder for appropriate enconding type.
public Decoder StreamDecode = Encoding.UTF8.GetDecoder();
public RequestState()
{
BufferRead = new byte[BufferSize];
RequestData = new StringBuilder(String.Empty);
Request = null;
ResponseStream = null;
}
}
public class WebRequester
{
private void ExecuteRequest()
{
RequestState requestState = new RequestState();
WebRequest request = WebRequest.Create("mysite");
request.BeginGetResponse(new AsyncCallback(Callback), requestState);
}
private void Callback(IAsyncResult ar)
{
// Get the RequestState object from the async result.
RequestState rs = (RequestState) ar.AsyncState;
// Get the WebRequest from RequestState.
WebRequest req = rs.Request;
// Call EndGetResponse, which produces the WebResponse object
// that came from the request issued above.
WebResponse resp = req.EndGetResponse(ar);
}
}
... based on this: http://msdn.microsoft.com/en-us/library/86wf6409(v=vs.71).aspx
Ok, I finally managed to write my own solution. We basically need a RequestState, a Callback Method and a TimeOut Thread. Here I'll just copy what was done in UnifyCommunity (now called unity3d wiki). This is outdated code, but smaller than what's there, so more convenient to show something here. Now I've removed (in the unit3d wiki) System.Action and static for performance and simplicity:
Usage
static public ThisClass Instance;
void Awake () {
Instance = GetComponent<ThisClass>();
}
static private IEnumerator CheckAvailabilityNow () {
bool foundURL;
string checkThisURL = "http://www.example.com/index.html";
yield return Instance.StartCoroutine(
WebAsync.CheckForMissingURL(checkThisURL, value => foundURL = !value)
);
Debug.Log("Does "+ checkThisURL +" exist? "+ foundURL);
}
WebAsync.cs
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Collections;
using UnityEngine;
/// <summary>
/// The RequestState class passes data across async calls.
/// </summary>
public class RequestState
{
public WebRequest webRequest;
public string errorMessage;
public RequestState ()
{
webRequest = null;
errorMessage = null;
}
}
public class WebAsync {
const int TIMEOUT = 10; // seconds
/// <summary>
/// If the URLs returns 404 or connection is broken, it's missing. Else, we suppose it's fine.
/// </summary>
/// <param name='url'>
/// A fully formated URL.
/// </param>
/// <param name='result'>
/// This will bring 'true' if 404 or connection broken and 'false' for everything else.
/// Use it as this, where "value" is a System sintaxe:
/// value => your-bool-var = value
/// </param>
static public IEnumerator CheckForMissingURL (string url, System.Action<bool> result) {
result(false);
Uri httpSite = new Uri(url);
WebRequest webRequest = WebRequest.Create(httpSite);
// We need no more than HTTP's head
webRequest.Method = "HEAD";
RequestState requestState = new RequestState();
// Put the request into the state object so it can be passed around
requestState.webRequest = webRequest;
// Do the actual async call here
IAsyncResult asyncResult = (IAsyncResult) webRequest.BeginGetResponse(
new AsyncCallback(RespCallback), requestState);
// WebRequest timeout won't work in async calls, so we need this instead
ThreadPool.RegisterWaitForSingleObject(
asyncResult.AsyncWaitHandle,
new WaitOrTimerCallback(ScanTimeoutCallback),
requestState,
(TIMEOUT *1000), // obviously because this is in miliseconds
true
);
// Wait until the the call is completed
while (!asyncResult.IsCompleted) { yield return null; }
// Deal up with the results
if (requestState.errorMessage != null) {
if ( requestState.errorMessage.Contains("404") || requestState.errorMessage.Contains("NameResolutionFailure") ) {
result(true);
} else {
Debug.LogWarning("[WebAsync] Error trying to verify if URL '"+ url +"' exists: "+ requestState.errorMessage);
}
}
}
static private void RespCallback (IAsyncResult asyncResult) {
RequestState requestState = (RequestState) asyncResult.AsyncState;
WebRequest webRequest = requestState.webRequest;
try {
webRequest.EndGetResponse(asyncResult);
} catch (WebException webException) {
requestState.errorMessage = webException.Message;
}
}
static private void ScanTimeoutCallback (object state, bool timedOut) {
if (timedOut) {
RequestState requestState = (RequestState)state;
if (requestState != null)
requestState.webRequest.Abort();
} else {
RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)state;
if (registeredWaitHandle != null)
registeredWaitHandle.Unregister(null);
}
}
}
I got threading to work on iOS- I believe it was crashing due to ghost threads or something. Restarting the device seems to have fixed the crashing so I'll just use WebRequest.HttpWebRequest with threads.
There is a way of doing this asynchronously, without using IEnumerator and yield return stuff. Check out the eDriven framework.
HttpConnector class: https://github.com/dkozar/eDriven/blob/master/eDriven.Networking/Rpc/Core/HttpConnector.cs
I've been using JsonFX with HttpConnector all the time, for instance in this WebPlayer demo: http://edrivenunity.com/load-images
Not having PUT and DELETE is not a big issue, since all of it could be done using GET and POST. For instance I'm successfully communicating with Drupal CMS using its REST service.
// javascript in the web player not ios, android or desktop you could just run the following code:
var jscall:String;
jscall="var reqScript = document.createElement('script');";
jscall+="reqScript.src = 'synchmanager_secure2.jsp?userid="+uid+"&token="+access_token+"&rnd='+Math.random()*777;";
jscall+="document.body.appendChild(reqScript);";
Application.ExternalEval(jscall);
// cs
string jscall;
jscall="var reqScript = document.createElement('script');";
jscall+="reqScript.src = 'synchmanager_secure2.jsp?userid="+uid+"&token="+access_token+"&rnd='+Math.random()*777;";
jscall+="document.body.appendChild(reqScript);";
Application.ExternalEval(jscall);
// then update your object using the your return in a function like this
// json return object always asynch
function sendMyReturn(args){
var unity=getUnity();
unity.SendMessage("object", "function", args );
}
sendMyReturn(args);
or you can send it via a AJAX function prewritten custom headers for security purposes
with this you would need signed headers and a signed request back from the server
I prefer md5 signatures comparatively they are not so big
Trying to implement downloadStringAsync() to prevent UI freezing for 10 seconds when downloading one byte of data. However, even though the download completes, it is freezing the UI just as if I used downloadString().
Here is my code:
public void loadHTML()
{
WebClient client = new WebClient();
// Specify that the DownloadStringCallback2 method gets called
// when the download completes.
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(loadHTMLCallback);
client.DownloadStringAsync(new Uri("http://www.example.com"));
return;
}
public void loadHTMLCallback(Object sender, DownloadStringCompletedEventArgs e)
{
// If the request was not canceled and did not throw
// an exception, display the resource.
if (!e.Cancelled && e.Error == null)
{
string result = (string)e.Result;
// Do cool stuff with result
}
}
Encountered the same problem, and found a solution.
Quite complex discussion here:
http://social.msdn.microsoft.com/Forums/en-US/a00dba00-5432-450b-9904-9d343c11888d/webclient-downloadstringasync-freeze-my-ui?forum=ncl
In short, the problem is web client is searching for proxy servers and hanging the app.
The following solution helps:
WebClient webClient = new WebClient();
webClient.Proxy = null;
... Do whatever else ...