WWW freezing when there is no internet - c#

I am writing a simple code in Unity, to check if I am able to reach a website through my app. This is the code I have written:
IEnumerator CheckInternetPing()
{
WWW wwwInternet = new WWW("http://google.com");
yield return wwwInternet;
if (wwwInternet.bytesDownloaded == 0)
{
//yield return new WaitForSeconds(1f);
Debug.Log("Not Connected to Internet");
}
else
{
Debug.Log("Connected to Internet");
internetMenu.SetActive(false);
}
}
I have found a bug where if I run this with my Internet on, it shows "Connected", but when I switch the Internet off and run the app immediately after, it logs nothing. It shows "Not Connected" only if I restart the app another time.
Does anyone know why it logs nothing during the first time? Thanks

This is a bug with the WWW class and has been here for a long time. The behavior is probably different every device. It used to freeze on the Editor if Wifi is disabled. A quick test showed that this bug has not been fixed.
You need to use HttpWebRequest instead of WWW.
In the example below, Thread is used to avoid the request blocking Unity program and UnityThread is used to make callback into Unity main Thread when the request is done. Get UnityThread from this post.
void Awake()
{
//Enable Callback on the main Thread
UnityThread.initUnityThread();
}
void isOnline(Action<bool> online)
{
bool success = true;
//Use ThreadPool to avoid freezing
ThreadPool.QueueUserWorkItem(delegate
{
try
{
int timeout = 2000;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://google.com");
request.Method = "GET";
request.Timeout = timeout;
request.KeepAlive = false;
request.ServicePoint.Expect100Continue = false;
request.ServicePoint.MaxIdleTime = timeout;
//Make sure Google don't reject you when called on mobile device (Android)
request.changeSysTemHeader("User-Agent", "Mozilla / 5.0(Windows NT 10.0; WOW64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 55.0.2883.87 Safari / 537.36");
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response == null)
{
success = false;
}
if (response != null && response.StatusCode != HttpStatusCode.OK)
{
success = false;
}
}
catch (Exception)
{
success = false;
}
//Do the callback in the main Thread
UnityThread.executeInUpdate(() =>
{
if (online != null)
online(success);
});
});
}
You need the extension class for the changeSysTemHeader function that allows the "User-Agent" header to be changed:
public static class ExtensionMethods
{
public static void changeSysTemHeader(this HttpWebRequest request, string key, string value)
{
WebHeaderCollection wHeader = new WebHeaderCollection();
wHeader[key] = value;
FieldInfo fildInfo = request.GetType().GetField("webHeaders",
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.GetField);
fildInfo.SetValue(request, wHeader);
}
}
It is really simple to use:
void Start()
{
isOnline((online) =>
{
if (online)
{
Debug.Log("Connected to Internet");
//internetMenu.SetActive(false);
}
else
{
Debug.Log("Not Connected to Internet");
}
});
}

Related

Gameobject not being set active in 'catch'. I've checked everything

Here is what I'm trying to do:
Trying to check if there's internet connection or no. If there is no internet connection, I want to set active a panel showing 'no internet connection'.
I dragged the panel to the inspector. The script which sets this panel active is on a gameobject which is active in hierarchy.
Here is the code:
public GameObject NoInternetConnectionPanel;
private void Start()
{
//Check Internet Connection
InvokeRepeating("CheckInternetConnection", 0f, 1f);
}
private void CheckInternetConnection()
{
var request = WebRequest.Create("http://google.com");
request.Method = "HEAD";
request.BeginGetResponse(result =>
{
try
{
using (var response = request.EndGetResponse(result) as HttpWebResponse)
{
if (response.StatusCode == HttpStatusCode.OK)
{
Debug.Log("Internet connection available");
}
/*else
{
Debug.Log("No internet connection");
}*/
}
}
catch (WebException ex)
{
Debug.Log("No internet connection");
NoInternetConnectionPanel.SetActive(true);
}
}, null);
StartCoroutine(WaitAndCheck());
}
private IEnumerator WaitAndCheck()
{
yield return new WaitForSeconds(1f);
}
The debug log message "no internet connection" is being shown in console. But the 'NoInternetConnectionPanel.SetActive(true)' is not working.
What could be the issue?
That's most probably because it is being executed on another any thread which is not supported by most of the Unity API. In general you shouldn't be using WebRequest at all but rather use UnityWebRequest in a Coroutine.
Also not btw that your WaitAndCheck routine doesn't do anything at all.
I would rather use something like e.g.
public GameObject NoInternetConnectionPanel;
private IEnumerator Start()
{
while(true)
{
yield return CheckInternetConnection();
yield return new WaitForSeconds(1f);
}
}
private IEnumerator CheckInternetConnection()
{
using(var request = UnityWebRequest.Head("http://google.com"))
{
yield return request.SendWebRequest();
var connection = request.result == UnityWebRequest.Result.Success;
NoInternetConnectionPanel.SetActive(! connection);
if(connection)
{
Debug.Log("Internet connection available");
}
else
{
Debug.LogWarning("No internet connection");
}
}
}
Instead of using a we request at all you can also use a simple Ping which is often used with e.g. 8.8.8.8, Google's global DNS server.
private IEnumerator CheckInternetConnection()
{
var ping = new Ping("8.8.8.8");
var maxPingTime = 1f; // maximum time to wait before declaring the ping as failed
var connection = false;
for(var timePassed = 0f; timePassed <= maxPingTime; timePassed += Time.deltaTime)
{
if(ping.isDone)
{
connection = true;
break;
}
}
NoInternetConnectionPanel.SetActive(! connection);
if(connection)
{
Debug.Log("Internet connection available");
}
else
{
Debug.LogWarning("No internet connection");
}
}

DispatchTimer blocks UI

Hi All: I want to run a function to check internet connection and update the UI content, so i'm using a Dispatchtimer in the WPF loaded, during the intenet check if the ping is blocked by the local server or for some x reasons the UI is blocking.
How can i call the function continuosly without blocking the UI & update the User interface? thanks.
private DispatcherTimer BackgroundAsyncTasksTimer;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
BackgroundAsyncTasksTimer = new DispatcherTimer();
BackgroundAsyncTasksTimer.Interval = TimeSpan.FromMilliseconds(2000);
BackgroundAsyncTasksTimer.Tick += BackgroundAsyncTasksTimer_Tick;
BackgroundAsyncTasksTimer.Start();
}
private async void BackgroundAsyncTasksTimer_Tick(object sender, object e)
{
if(CanConnectToTheInternet())
{
Dispatcher.Invoke((Action)delegate () {
einternetcoxn.Fill = (SolidColorBrush)new BrushConverter().ConvertFromString("#00ff00"); //Eclipse
checkNewversion();
bUpdatesoftware.IsEnabled = true;//button
});
}
else
{
Dispatcher.Invoke((Action)delegate () {
einternetcoxn.Fill = (SolidColorBrush)new BrushConverter().ConvertFromString("#841c34");
clearfields();
});
}
}
private static bool CanConnectToTheInternet()
{
try
{
string[] strArray = new string[5]
{
"8.8.8.8",
"https://www.google.com",
"https://www.microsoft.com",
"https://www.facebook.com",
};
if (((IEnumerable<string>)strArray).AsParallel<string>().Any<string>((Func<string, bool>)(url =>
{
try
{
Ping ping = new Ping();
byte[] buffer = new byte[32];
PingOptions options = new PingOptions();
if (ping.Send(url, 500, buffer, options).Status == IPStatus.Success)
return true;
}
catch
{
}
return false;
})))
return true;
if (((IEnumerable<string>)strArray).AsParallel<string>().Any<string>((Func<string, bool>)(url =>
{
try
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.KeepAlive = false;
httpWebRequest.Timeout = 5000;
using ((HttpWebResponse)httpWebRequest.GetResponse())
return true;
}
catch
{
}
return false;
})))
return true;
}
catch
{
return false;
}
return false;
}
A DispatcherTimeris not running the tick event on a background thread, at least not by default in a UI application.
But this should be fine if you change your CanConnectToTheInternetmethod to use Ping.SendAsync and WebRequest.GetResponseAsync. That will require you to follow the async await pattern, but this is an good example of the kind of task this pattern is meant for. In this case you should get rid of all the Dispatcher.Invoke-stuff, since all of your code would run on the UI thread.
The alternative would be to use a timer that runs the tick-event on a threadpool thread, like Timers.Timer. See also timer comparison

C# WinForm multiple consecutive threaded web requests in a row

I have written a little tool in C# (WinForm) using Visual Studio 2012.
Its purpose is to let users check a given City and ZIP code against a database to validate if the city/zip combination is legit.
Everything works well so far (see the simplyfied code below). Since it is possible that users carry out more than one validation simultaneously, I've used a queue pattern (to make sure, I am sending the multiple requests in the right order, as they have been submitted).
Now, I need to modify my code to run 3 web requests to achive the same result.
Instead of just querying a webservice directly with city and zip code, I now need to make 3 requests in consecutive order:
send a code to a webserver X to request a key
send the obtained key to webserver X to validate city and zip code
send the validated result to webserver Y to save the city and zip code.
Obviously, I have to wait for the 1st request to be answered before I can proceed to carry out the 2nd request, and so on... And I need to check if the 2nd request returned true/valid otherwise I do not need to save/carry out the third request.
I need help on how to include these two more web requests in my code without breaking my queue mechanism. One item in my queue should now contain the 3 requests whilst making sure I am not requesting step 2 or 3 prior to step 1 or step 2 having delivered a (valid) result.
Here is what I've got so far:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading;
using System.Web;
using System.Xml;
using System.Globalization;
using System.Security.Cryptography;
using System.Windows.Forms;
using CheckAddress.Util;
namespace CheckAddress.Api
{
public class ApiHelper
{
#region singleton
private static ApiHelper _instance;
private static Object _lock = new object();
public static ApiHelper Instance
{
get
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ApiHelper();
}
}
return _instance;
}
}
#endregion
private Thread _queueThread;
private bool _running;
private Queue<Address> _queue;
private ApiHelper()
{
_running = false;
_queue = new Queue<Address>();
MaxThreads = SettingsFile.Instance.MaxThreads;
MaxAttempts = SettingsFile.Instance.MaxAttempts;
ThreadPool.SetMaxThreads(MaxThreads, MaxThreads);
}
public int MaxThreads { get; set; }
public int MaxAttempts { get; set; }
public void Add(Address a)
{
lock (_queue)
{
_queue.Enqueue(a);
}
}
private void AddressSendProc(object state)
{
bool isValid = false;
foreach (Address.CityZIP theCityZIP in a.listOfAddresses)
{
while (!success && tries < MaxAttempts)
{
try
{
var req = (HttpWebRequest)WebRequest.Create("http://domain.com/geo.php?city=auburn&zip=95602");
req.Method = "GET";
req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0";
req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
req.ContentType = "application/x-www-form-urlencoded";
req.Timeout = 3000;
var resp = (HttpWebResponse)req.GetResponse();
var ms = new MemoryStream();
var rs = resp.GetResponseStream();
if (rs != null)
{
var buf = new byte[4096];
int len = 0;
while ((len = rs.Read(buf, 0, buf.Length)) > 0)
{
ms.Write(buf, 0, len);
}
rs.Close();
}
var xml = new XmlDocument();
xml.LoadXml(Encoding.UTF8.GetString(ms.ToArray()));
var nodes = xml.SelectNodes("/response/valid");
if (nodes.Count == 1)
{
isValid = true;
}
int code = 0;
Int32.TryParse(nodes[0].InnerText, out code);
if (code == 200)
{
success = true;
}
}
catch (Exception ex)
{
/* */
}
tries++;
}
}
}
private void ApiThreadProc()
{
while (_running)
{
try
{
lock (_queue)
{
while (_queue.Count > 0)
{
ThreadPool.QueueUserWorkItem(AddressSendProc, _queue.Dequeue());
}
}
Thread.Sleep(1000);
}
catch (Exception) {/*ignore*/}
}
}
public void Start()
{
_running = true;
_queueThread = new Thread(ApiThreadProc);
_queueThread.Start();
}
public void Stop()
{
_running = false;
if (_queueThread != null)
{
_queueThread.Interrupt();
}
}
}
}

Code to be run on program close and pc shutdown

I have a formless application that runs as a system tray application. Im struggling with it having to run code when the computer closes or when the application is closed.
This is what i have so far
class sysTrayIcon
{
public sysTrayIcon()
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(exitCode);
SystemEvents.SessionEnding += new SessionEndingEventHandler(exitCode);
}
public void exitCode(object sender, EventArgs e)
{
contactCman appClosing = new contactCman();
bool didWeSuceed = appClosing.stampUserOut();
if (didWeSuceed == false)
{
MessageBox.Show("Stamp out function run returned fail!!");
}
else
{
MessageBox.Show("Stamp out function run returned success");
}
}
}
class contactCman
{
public Dictionary<string, string> getUrlResponse(string toDo)
{
var url = mainSiteUrl + "/" + apiFileName + "?" + "userid=" + userid + "&pw=" + pw + "&toDo=" + toDo;
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
WebHeaderCollection header = response.Headers;
var encoding = ASCIIEncoding.ASCII;
string responseText;
using (var reader = new System.IO.StreamReader(response.GetResponseStream(), encoding))
{
responseText = reader.ReadToEnd();
}
// key1=value1;key2=value2;key3=value3;
var responseData = responseText.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(part => part.Split('='))
.ToDictionary(split => split[0], split => split[1]);
return responseData;
}
public Dictionary<string, string> checkLastStamp()
{
string toDo = "getLastStamp";
Dictionary<string, string> urlResponse = getUrlResponse(toDo);
return urlResponse;
}
public bool stampUserOut()
{
Dictionary<string, string> usersLastStamp = checkLastStamp();
string valueOfKey;
if (usersLastStamp.TryGetValue("checkInOut", out valueOfKey) && (valueOfKey == "in"))
{
string toDo = "stampUserOut";
Dictionary<string, string> urlResponse = getUrlResponse(toDo);
if (urlResponse.TryGetValue("Message", out valueOfKey) && (valueOfKey == "Success!"))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
My problem is that the code above works (only tested with the program close though) as intended when run through the debugger inside visual studio. If i run it outside, the messagebox never appears.
I think it might be better to listen to the ApplicationExit event. It will prevent the application from actually exiting until the handler is executed:
static void Main()
{
Application.ApplicationExit += Application_ApplicationExit;
...
}
static void Application_ApplicationExit(object sender, EventArgs e)
{
MessageBox.Show("Exit!");
}
SessionEnding does not fire if you have no window. See http://msdn.microsoft.com/en-us/library/microsoft.win32.systemevents.sessionending%28v=vs.110%29.aspx
ProcessExit only has 2 seconds to run. It is too late to display a message box or do anything interactive:
http://msdn.microsoft.com/en-us/library/system.appdomain.processexit%28v=vs.110%29.aspx
I think you either need a service so that it can stay alive after login/logout, or you need to create a window so you have a message pump.

HttpWebRequest from AudioPlayerAgent

I'm creating an app that plays an endless audio stream. There is a separate web service that I can query to get the title and artist of the currently playing track. What I want to do is query that service every 20 seconds and then set the track title/artist accordingly. Currently I'm using a background AudioPlayerAgent so that the stream can be played outside of my application. Here is the code I have so far:
public AudioPlayer()
{
if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += AudioPlayer_UnhandledException;
});
trackTimer = new Timer(TrackTimerTick, null, 1000, 5000);
}
}
public void TrackTimerTick(object state) {
// Create a HttpWebrequest object to the desired URL.
HttpWebRequest trackRequest = (HttpWebRequest)HttpWebRequest.Create("<stream url>");
// Start the asynchronous request.
IAsyncResult result = (IAsyncResult)trackRequest.BeginGetResponse(new AsyncCallback(TrackCallback), trackRequest);
}
public void TrackCallback(IAsyncResult result) {
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing && result != null) {
try {
// State of request is asynchronous.
HttpWebRequest trackRequest = (HttpWebRequest)result.AsyncState;
HttpWebResponse trackResponse = (HttpWebResponse)trackRequest.EndGetResponse(result);
using (StreamReader httpwebStreamReader = new StreamReader(trackResponse.GetResponseStream())) {
string results = httpwebStreamReader.ReadToEnd();
StringReader str = new StringReader(results);
XDocument trackXml = XDocument.Load(str);
string title = (from t in trackXml.Descendants("channel") select t.Element("title").Value).First<string>();
string artist = (from t in trackXml.Descendants("channel") select t.Element("artist").Value).First<string>();
if (BackgroundAudioPlayer.Instance.Track != null) {
AudioTrack track = BackgroundAudioPlayer.Instance.Track;
track.BeginEdit();
track.Title = title;
track.Artist = artist;
track.EndEdit();
}
}
trackResponse.Close();
NotifyComplete();
} catch (WebException e) {
Debug.WriteLine(e);
Debug.WriteLine(e.Response);
} catch (Exception e) {
Debug.WriteLine(e);
}
}
}
A web exception is thrown anytime that I try to read the response from the HttpWebRequest. Is this the right way to do this? Does anyone have suggestions as to how I can fix this?
You're not closing the HttpWebResponse, which is a necessity. Also, there's an overload of XDocument.Load() that takes a Stream so you don't need to use a StreamReader at all.
EDIT: Sorry, I overlooked the Close() call at the end. But the other comment still applies.
If it doesn't solve the problem, atleast it makes your code look cleaner:
public void TrackCallback(IAsyncResult result) {
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing && result != null) {
try {
// State of request is asynchronous.
HttpWebRequest trackRequest = (HttpWebRequest)result.AsyncState;
using (HttpWebResponse trackResponse = (HttpWebResponse)trackRequest.EndGetResponse(result)){
XDocument trackXml = XDocument.Load(trackResponse.GetResponseStream());
string title = (from t in trackXml.Descendants("channel") select t.Element("title").Value).First<string>();
string artist = (from t in trackXml.Descendants("channel") select t.Element("artist").Value).First<string>();
if (BackgroundAudioPlayer.Instance.Track != null) {
AudioTrack track = BackgroundAudioPlayer.Instance.Track;
track.BeginEdit();
track.Title = title;
track.Artist = artist;
track.EndEdit();
}
}
}
NotifyComplete();
} catch (WebException e) {
Debug.WriteLine(e);
Debug.WriteLine(e.Response);
} catch (Exception e) {
Debug.WriteLine(e);
}
}
}
This has to do with the AudioPlayer going out of scope after it begins playing the music. The AudioPlayer only lives for a fraction of time and it terminated after the call to NotifyComplete
Take a look at my reply to this post:
AudioPlayerAgent, timer and webservice
More info:
The background audio thread will "suspend" after NotifyComplete is called. The way back in is when the user changes play (OnUserAction), or when the song ends (OnPlayStateChanged). If you will continue to play, get the new info within the OnPlayStateChanged method.

Categories

Resources