How to get DateTime from the internet? - c#-4.0

How to get current date and time from internet or server using C#? I am trying to get time as follows:
public static DateTime GetNetworkTime (string ntpServer)
{
IPAddress[] address = Dns.GetHostEntry(ntpServer).AddressList;
if (address == null || address.Length == 0)
throw new ArgumentException("Could not resolve ip address from '" + ntpServer + "'.", "ntpServer");
IPEndPoint ep = new IPEndPoint(address[0], 123);
return GetNetworkTime(ep);
}
I am passing server IP address as netServer, but it does not work properly.

Here is code sample that you can use to retrieve time from NIST Internet Time Service
var client = new TcpClient("time.nist.gov", 13);
using (var streamReader = new StreamReader(client.GetStream()))
{
var response = streamReader.ReadToEnd();
var utcDateTimeString = response.Substring(7, 17);
var localDateTime = DateTime.ParseExact(utcDateTimeString, "yy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}

Here is a quick code to get the time from the header, works without the need of port 13
public static DateTime GetNistTime()
{
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://www.microsoft.com");
var response = myHttpWebRequest.GetResponse();
string todaysDates = response.Headers["date"];
return DateTime.ParseExact(todaysDates,
"ddd, dd MMM yyyy HH:mm:ss 'GMT'",
CultureInfo.InvariantCulture.DateTimeFormat,
DateTimeStyles.AssumeUniversal);
}

Things could go wrong. All implements of the code founded above are prone to errors. Sometimes, it works and sometimes it trows a WebExpection error message.
A better implementation:
try{
using (var response =
WebRequest.Create("http://www.google.com").GetResponse())
//string todaysDates = response.Headers["date"];
return DateTime.ParseExact(response.Headers["date"],
"ddd, dd MMM yyyy HH:mm:ss 'GMT'",
CultureInfo.InvariantCulture.DateTimeFormat,
DateTimeStyles.AssumeUniversal);
}
catch (WebException)
{
return DateTime.Now; //In case something goes wrong.
}
Conclusion:
Having your web app depend on a service that provides accurate date information is critical. I have used one of the code founded here in my app and it really mess things up.

One more version of the same idea:
public static class InternetTime
{
public static DateTimeOffset? GetCurrentTime()
{
using (var client = new HttpClient())
{
try
{
var result = client.GetAsync("https://google.com",
HttpCompletionOption.ResponseHeadersRead).Result;
return result.Headers.Date;
}
catch
{
return null;
}
}
}
}
Here HttpCompletionOption.ResponseHeadersRead is used to prevent loading of the rest of the response, as we need only HTTP headers.
Use InternetTime.GetCurrentTime().Value.ToLocalTime() to get current local time.

Important: first check the avaible servers on
NIST Internet Time Servers.
public static DateTime GetServerTime()
{
var result = DateTime.Now;
// Initialize the list of NIST time servers
// http://tf.nist.gov/tf-cgi/servers.cgi
string[] servers = new string[] {
"time-c.nist.gov",
"time-d.nist.gov",
"nist1-macon.macon.ga.us",
"wolfnisttime.com",
"nist.netservicesgroup.com",
"nisttime.carsoncity.k12.mi.us",
"nist1-lnk.binary.net",
"wwv.nist.gov",
"time.nist.gov",
"utcnist.colorado.edu",
"utcnist2.colorado.edu",
"nist-time-server.eoni.com",
"nist-time-server.eoni.com"
};
Random rnd = new Random();
foreach (string server in servers.OrderBy(x => rnd.NextDouble()).Take(9))
{
try
{
// Connect to the server (at port 13) and get the response. Timeout max 1second
string serverResponse = string.Empty;
var tcpClient = new TcpClient();
if (tcpClient.ConnectAsync(server, 13).Wait(1000))
{
using (var reader = new StreamReader(tcpClient.GetStream()))
{
serverResponse = reader.ReadToEnd();
}
}
// If a response was received
if (!string.IsNullOrEmpty(serverResponse))
{
// Split the response string ("55596 11-02-14 13:54:11 00 0 0 478.1 UTC(NIST) *")
string[] tokens = serverResponse.Split(' ');
// Check the number of tokens
if (tokens.Length >= 6)
{
// Check the health status
string health = tokens[5];
if (health == "0")
{
// Get date and time parts from the server response
string[] dateParts = tokens[1].Split('-');
string[] timeParts = tokens[2].Split(':');
// Create a DateTime instance
DateTime utcDateTime = new DateTime(
Convert.ToInt32(dateParts[0]) + 2000,
Convert.ToInt32(dateParts[1]), Convert.ToInt32(dateParts[2]),
Convert.ToInt32(timeParts[0]), Convert.ToInt32(timeParts[1]),
Convert.ToInt32(timeParts[2]));
// Convert received (UTC) DateTime value to the local timezone
result = utcDateTime.ToLocalTime();
return result;
// Response successfully received; exit the loop
}
}
}
}
catch
{
// Ignore exception and try the next server
}
}
return result;
}

public static Nullable<DateTime> GetDateTime()
{
Nullable<DateTime> dateTime = null;
System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.WebRequest.Create("http://www.microsoft.com");
request.Method = "GET";
request.Accept = "text/html, application/xhtml+xml, */*";
request.UserAgent = "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)";
request.ContentType = "application/x-www-form-urlencoded";
request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
try
{
System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string todaysDates = response.Headers["date"];
dateTime = DateTime.ParseExact(todaysDates, "ddd, dd MMM yyyy HH:mm:ss 'GMT'",
System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat, System.Globalization.DateTimeStyles.AssumeUniversal);
}
}
catch
{
dateTime = null;
}
return dateTime;
}

Related

What's wrong on using HttpWebRequest this way?

This is the way I do server request to some endpoints from Azure:
public T SingleRead<T>(string url, string method, object entity = null)
{
T returnValue = default(T);
var resp = GetRESTResponse(url, method, entity);
string responseText = GetResponseText(resp);
try
{
returnValue = JsonConvert.DeserializeObject<T>(responseText);
}
catch (Exception ex)
{
return default(T);
}
return returnValue;
}
private HttpWebResponse GetRESTResponse(string url, string method, object entity = null)
{
var address;
if (!url.StartsWith("http"))
{
if (!url.StartsWith("/")) url = $"/{url}";
address = baseAddress + url;
}
else
{
address = url;
}
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(address);
req.Method = method;
if (entity != null)
{
byte[] byteArray = Encoding.Default.GetBytes(JsonConvert.SerializeObject(entity));
req.ContentLength = byteArray.Length;
req.ContentType = "application/json";
Stream dataStream = req.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Flush();
dataStream.Close();
}
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
Log(e.Reponse);
resp = (HttpWebResponse)e.Response;
}
return resp;
}
private static string GetResponseText(HttpWebResponse resp)
{
var encoding = Encoding.ASCII;
string responseText = ".";
using (var reader = new StreamReader(resp.GetResponseStream(), encoding))
{
responseText = reader.ReadToEnd();
}
if (resp.StatusCode == HttpStatusCode.InternalServerError || resp.StatusCode == HttpStatusCode.BadRequest || resp.StatusCode == HttpStatusCode.NotFound)
{
return "";
}
return responseText;
}
It works quite often. Sometimes, it doesn't, and I get the a "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond" error from the Log.
Is there some wrong in the procedure, or it could be a "timeout" by endpoint?
The called server users says "we don't have any problem, and we don't get the request".
Not sure if its the fault of the code above (maybe some stream not closed?). But I don't see any problem. Do you see any trouble in this?
It can be a network issue, which fails quite often. You need to remember there are several switches until the request goes outside the datacenter, and there are countless requests happening at the same time (you, and all other Azure customers).
This can be a transient fault, which may work if you send another request. You need to implement some retry logic to identify if the failure is transient or not.
More info:
https://learn.microsoft.com/en-us/azure/architecture/patterns/retry
And here's a sample using Polly, which is strongly recommended and even used inside Azure SDKs:
https://stackoverflow.com/a/66554740/1384539
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly
https://github.com/App-vNext/Polly

Making HTTPS call in C# with the BouncyCastle library

Using C# 4.0, I need to make HTTPS call with the BouncyCastle library (Short story : Windows XP + TLS 1.2).
When using the following code, I get a "HTTP Error 400. The request verb is invalid."
Here is my code :
using (var client = new TcpClient("serverName", 443))
{
var sr = new SecureRandom();
var cl = new MyTlsClient();
var protocol = new TlsClientProtocol(client.GetStream(), sr);
protocol.Connect(new MyTlsClient());
using (var stream = protocol.Stream)
{
var hdr = new StringBuilder();
hdr.AppendLine("GET /Url/WebService.asmx?wsdl HTTP/1.1");
hdr.AppendLine("Host: serverName");
hdr.AppendLine("Content-Type: text/xml; charset=utf-8");
hdr.AppendLine("Connection: close");
hdr.AppendLine();
var dataToSend = Encoding.ASCII.GetBytes(hdr.ToString());
sr.NextBytes(dataToSend);
stream.Write(dataToSend, 0, dataToSend.Length);
int totalRead = 0;
string response = "";
byte[] buff = new byte[1000];
do
{
totalRead = stream.Read(buff, 0, buff.Length);
response += Encoding.ASCII.GetString(buff, 0, totalRead);
} while (totalRead == buff.Length);
}
}
class MyTlsClient : DefaultTlsClient
{
public override TlsAuthentication GetAuthentication()
{
return new MyTlsAuthentication();
}
}
class MyTlsAuthentication : TlsAuthentication
{
public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest) { return null; }
public void NotifyServerCertificate(Certificate serverCertificate) { }
}
What I've already done :
Using WireShark to decrypt the ssl stream and inspect the request send => I've never succeeded to decrypt ssl stream
Using fiddler to decrypt the https stream => No detection by fiddler so I suspect something might be badly encrypted
Any ideas ?
Thanks to PeterDettman who gave me the solution :
I must not use the sr.NextBytes(instructions), so the code becomes :
using (var client = new TcpClient("serverName", 443))
{
var sr = new SecureRandom();
var cl = new MyTlsClient();
var protocol = new TlsClientProtocol(client.GetStream(), sr);
protocol.Connect(new MyTlsClient());
using (var stream = protocol.Stream)
{
var hdr = new StringBuilder();
hdr.AppendLine("GET /Url/WebService.asmx?wsdl HTTP/1.1");
hdr.AppendLine("Host: serverName");
hdr.AppendLine("Content-Type: text/xml; charset=utf-8");
hdr.AppendLine("Connection: close");
hdr.AppendLine();
var dataToSend = Encoding.ASCII.GetBytes(hdr.ToString());
stream.Write(dataToSend, 0, dataToSend.Length);
int totalRead = 0;
string response = "";
byte[] buff = new byte[1000];
do
{
totalRead = stream.Read(buff, 0, buff.Length);
response += Encoding.ASCII.GetString(buff, 0, totalRead);
} while (totalRead == buff.Length);
}
}

How to get Logic App Metrics?

I'm trying to get Logic Apps metrics like BillableExecutions, Latency etc in my console application.
Currently I'm able to list the logic apps runs, triggers, versions using the .Net Client Microsoft.Azure.Management. But it doesn't seem to have the API to access the monitoring API's.
Code excerpt
private static void Main(string[] args)
{
var token = GetTokenCredentials();
var client = new LogicManagementClient(token, new HttpClientHandler())
{
SubscriptionId = new AzureSubscription().SubscriptionId
};
var dataQuery = new ODataQuery<WorkflowFilter>
{
Top = 50
};
using (client)
{
var logicAppsWorkFlows = client.Workflows.ListBySubscription(dataQuery);
foreach (var logicAppsWorkFlow in logicAppsWorkFlows)
{
var runs = GetWorkflowRuns(client, logicAppsWorkFlow.Id.Split('/')[4], logicAppsWorkFlow.Name);
Console.WriteLine(runs.Count);
}
Console.WriteLine(logicAppsWorkFlows.Count());
}
}
Can someone tell me how to access Logic Apps Metrics? Is there a client similar to Microsoft.Azure.Management for access metrics data?
Update 2
I have found a client dll which was in pre release mode which is used to get metrics. Below is my current code
var token = GetTokenCredentials();
var insightsClient = new InsightsClient(token, new HttpClientHandler())
{
SubscriptionId = new AzureSubscription().SubscriptionId
};
var logicManagementClient = new LogicManagementClient(token, new HttpClientHandler())
{
SubscriptionId = new AzureSubscription().SubscriptionId
};
var dataQuery = new ODataQuery<WorkflowFilter>
{
Top = 50
};
using (logicManagementClient)
{
var logicAppsWorkFlows = logicManagementClient.Workflows.ListBySubscription(dataQuery);
foreach (var logicAppsWorkFlow in logicAppsWorkFlows)
{
using (insightsClient)
{
var metricsDataQuery = new ODataQuery<Metric>
{
Filter = "name.value eq 'ActionLatency' and startTime ge '2014-07-16'"
};
IEnumerable<Metric> metricsList = null;
try
{
metricsList = insightsClient.Metrics.List(logicAppsWorkFlow.Id, metricsDataQuery);
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (metricsList == null) continue;
foreach (var metric in metricsList)
{
foreach (var metricValue in metric.Data)
{
Console.WriteLine(metric.Name.Value + " = " + metricValue.Total);
}
}
}
}
}
I'm getting an exception saying the filter string is not valid. Im referring the filter string structure provided here
https://learn.microsoft.com/en-us/rest/api/monitor/filter-syntax
Can someone tell what im doing wrong here?
Thanks
It looks like ge is not allowed for Logic Apps StartTime field for some reason. I had to change the code to below to make it work
using (logicManagementClient)
{
var logicAppsWorkFlows = logicManagementClient.Workflows.ListBySubscription(dataQuery);
foreach (var logicAppsWorkFlow in logicAppsWorkFlows)
{
using (insightsClient)
{
var metricsDataQuery = new ODataQuery<Metric>
{
Filter = "startTime eq " + DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd") + " and name.value eq 'BillableTriggerExecutions' and endTime eq " + DateTime.Now.ToString("yyyy-MM-dd")
};
var query = metricsDataQuery.GetQueryString();
Console.WriteLine(query);
IEnumerable<Metric> metricsList = null;
try
{
//throws exception if there is no metrics data
//TODO: Check whether the logic app ran atleast one time
metricsList = insightsClient.Metrics.List(logicAppsWorkFlow.Id, metricsDataQuery);
}
catch (Exception e)
{
Console.WriteLine(e);
}
if (metricsList == null) continue;
foreach (var metric in metricsList)
{
foreach (var metricValue in metric.Data)
{
Console.WriteLine(metric.Name.Value + " = " + metricValue.Total);
}
}
}
}
}

WebRequest Threads Blocking at Two Requests

I need to test a Data Context and see what behavior it has under multiple simultaneous requests, for that I made a simple console application that [in theory] would send these requests:
private static DateTime startTime = DateTime.Now.AddSeconds(5);
public static Random rand = new Random();
static void Main(string[] args)
{
const byte testThreads = 10;
ThreadStart[] threadStarts = new ThreadStart[testThreads];
Thread[] threads = new Thread[testThreads];
for (byte i = 0; i < testThreads; i++)
{
threadStarts[i] = new ThreadStart(ExecutePOST);
threads[i] = new Thread(threadStarts[i]);
}
for (byte i = 0; i < testThreads; i++){ threads[i].Start(); }
for (byte i = 0; i < testThreads; i++){ threads[i].Join(); }
}
The called function is
private static void ExecutePOST()
{
while (DateTime.Now < startTime) { }
Console.WriteLine("{0} STARTING TEST", DateTime.Now.Millisecond);
WebRequest webRequest = WebRequest.Create(/*URL*/);
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
string name = string.Format("Test {0}", Program.rand.Next(1000));
byte[] bytes = Encoding.ASCII.GetBytes(/*PARAMETERS*/);
Stream output = null;
try
{
webRequest.ContentLength = bytes.Length;
output = webRequest.GetRequestStream();
output.Write(bytes, 0, bytes.Length);
Console.WriteLine("{0}:{1}", DateTime.Now.Millisecond, name);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (output != null)
{
output.Close();
}
}
}
The output I get is:
Can anyone please explain this behavior? Why is it stopping after two requests?
Thank you
Yes, this is because the number of connections per URL is limited to 2 by default - the connections are pooled.
You're hogging the connection by writing data to the request stream, but then never getting the response. A simple:
using (webRequest.GetResponse()) {}
at the end of the method is likely to sort it out. That will finish the request and release the connection to be used by another request.
Also note that a using statement for the output stream would make your code simpler too.

Recursive linkscraper c#

I'm struggling with this a whole day now and I can't seem to figure it out.
I have a fucntion that gives me a list of all links on a specific url. That works fine.
However I want to make this function recursive so that it searches for the links found with the first search and adds them to the list and continue so that it goes through all my pages on the website.
How can I make this recursive?
My code:
class Program
{
public static List<LinkItem> urls;
private static List<LinkItem> newUrls = new List<LinkItem>();
static void Main(string[] args)
{
WebClient w = new WebClient();
int count = 0;
urls = new List<LinkItem>();
newUrls = new List<LinkItem>();
urls.Add(new LinkItem{Href = "http://www.smartphoto.be", Text = ""});
while (urls.Count > 0)
{
foreach (var url in urls)
{
if (RemoteFileExists(url.Href))
{
string s = w.DownloadString(url.Href);
newUrls.AddRange(LinkFinder.Find(s));
}
}
urls = newUrls.Select(x => new LinkItem{Href = x.Href, Text=""}).ToList();
count += newUrls.Count;
newUrls.Clear();
ReturnLinks();
}
Console.WriteLine();
Console.Write("Found: " + count + " links.");
Console.ReadLine();
}
private static void ReturnLinks()
{
foreach (LinkItem i in urls)
{
Console.WriteLine(i.Href);
//ReturnLinks();
}
}
private static bool RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
//Getting the Web Response.
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
//Returns TURE if the Status code == 200
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
}
The code behind LinkFinder.Find can be found here: http://www.dotnetperls.com/scraping-html
Anyone knows how I can either make that function recursive or can I make the ReturnLinks function recursive? I prefer to not touch the LinkFinder.Find method as this works perfect for one link, I just should be able to call it as many times as needed to expand my final url list.
I assume you want to load each link and find the link within, and continue until you run out of links?
Since it is likely that the recursion depth could get very large, i would avoid recursion, this should work i think.
WebClient w = new WebClient();
int count = 0;
urls = new List<string>();
newUrls = new List<LinkItem>();
urls.Add("http://www.google.be");
while (urls.Count > 0)
{
foreach(var url in urls)
{
string s = w.DownloadString(url);
newUrls.AddRange(LinkFinder.Find(s));
}
urls = newUrls.Select(x=>x.Href).ToList();
count += newUrls.Count;
newUrls.Clear();
ReturnLinks();
}
Console.WriteLine();
Console.Write("Found: " + count + " links.");
Console.ReadLine();
static void Main()
{
WebClient w = new WebClient();
List<ListItem> allUrls = FindAll(w.DownloadString("http://www.google.be"));
}
private static List<ListItem> FindAll(string address)
{
List<ListItem> list = new List<ListItem>();
foreach (url in LinkFinder.Find(address))
{
list.AddRange(FindAll(url.Address)));//or url.ToString() or what ever the string that represents the address
}
return list;
}

Resources