I noticed, using Tracert on Windows, that if there is no data for DNS info, there is a delay too long to be acceptable - more or less 5 seconds. For this reason, I coded my own Tracert class with a latency parameter, waiting for an answer. All is done using a Task method. However, if I code for a few years, now I am trying to elaborate my code cleanly and efficiently. Please, can you tell me what I should change? Maybe you see some mistakes or non-senses. Thank you for your help ++
enter image description here
class TraceRoute
{
public void TraceRouteX(string ipAddressOrHostName, int hops = 30, int latency = 1000, bool dns = false)
{ // this way we can check if IP or HostName are valid
try
{ // a tip to get only IPv4
IPAddress ipAddress = Dns.GetHostAddresses(ipAddressOrHostName).First(address => address.AddressFamily == AddressFamily.InterNetwork);
using (Ping pingSender = new Ping())
{
PingOptions pingOptions = new PingOptions();
Stopwatch stopWatch = new Stopwatch();
byte[] bytes = new byte[32]; // 32 bits as packet
string hostName; // for DNS
PingReply pingReply;
pingOptions.DontFragment = true;
pingOptions.Ttl = 1;
int maxHops = hops;
// just a first message on screen before TraceRoute
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(string.Format(" TraceRoute to {0} with a maximum of {1} hops, setting latency to {2} and DNS feature to {3}\n", ipAddress, maxHops, latency, dns));
Console.ResetColor();
for (int i = 1; i < maxHops + 1; i++)
{ // for latence
stopWatch.Reset();
stopWatch.Start(); // ping options allows us to set TTL
pingReply = pingSender.Send(ipAddress, latency, new byte[32], pingOptions);
stopWatch.Stop();
if (dns)
{ // DNS resolving using a timer
Task<string> task = Task<string>.Factory.StartNew(() =>
{
var t = Dns.GetHostEntry(pingReply.Address).HostName;
return t.ToString(); // well well ?
});
// waiting an answer
bool success = task.Wait(latency);
if (success)
hostName = task.Result;
else
hostName = "TimeOut (no DNS info)";
}
else
hostName = "";
// display string with all informations - easy reading
if (i % 2 == 0)
Console.ForegroundColor = ConsoleColor.DarkYellow;
else
Console.ForegroundColor = ConsoleColor.Cyan;
// display final output
Console.WriteLine(string.Format(" h{0}\t{1} ms\t{2}\t\t{3}", i, stopWatch.ElapsedMilliseconds, pingReply.Address, hostName));
Console.ResetColor();
// status
if (pingReply.Status == IPStatus.Success)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("\n The IP target " + pingReply.Address + " has been reached");
Console.ResetColor();
break;
}
// increment TTL to get the next node
pingOptions.Ttl++;
}
}
}
catch
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(" ERROR: You Have Some TIMEOUT Issue Or An Invalid IP/Host");
Console.ResetColor();
}
}
}
Related
When I want to program a Teensy 3.5 micro-controller, I send it a .HEX file via a hardware serial port. I have two ways of doing this; one way is to use a serial comms app like Tera Term to send the file, and another way is via a small C# command-line app I wrote to do the same thing.
When I send a 3000+ line Hex file to the micro-controller, it takes approximately 14 seconds. When I do the same thing with my C# program, it takes twice as long (or longer!). The baud rate for both Tera Term and my C# program are the same, and of course the Teensy setup is common to both.
When I looked as the serial transfer using a digital scope, I see the following (same time scale in both photos):
The first photo shows the transfer when using Tera Term, and the second one shows the transfer when using my C# program using Serial.WriteLine() to transfer file contents line-by-line, as shown below:
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Timers;
using System.Diagnostics;
/*
Small console app to facilitate over-the-air (OTA) updates to a Teensy 3.x/4.x controller,
using VS2019 with the Visual Micro extension as the Arduino IDE. It is called by a post-build
'hook' statement in a file called 'board.txt' located in whatever Teensy program is
being updated. This app does the following:
- Extract the project path and selected COMPORT number from the arguments to the call to Main()
- Opens a UART serial port connection to the Teensy, typically one provided by a BT adaptor
operating in 'pass-through' mode. The serial port COMPORT number is passed into this app
as an argument.
- Sends whatever command is required to put the existing Teensy firmware into 'update' mode
- Using the path of the updating program (passed in as an argument), locates the .HEX file
associated with the project, and sends it's contents to the Teensy, one line at a time, counting
lines and confirming checksums line-by-line
- Compares the number of lines sent to the Teensy with the number of lines received by the Teensy,
and if there is a match, allows the Teensy update process to complete; otherwise aborts
*/
namespace TeensyFlash
{
class Program
{
const string startCmdStr = "U"; //used in sketch's 'GetUserInput()' to start upload sequence
static string rcvStr = string.Empty;
private static System.Timers.Timer aTimer;
private static bool bTimedOut;
static void Main(string[] args)
{
//Extract the build path and selected COMPORT number from the arguments to the call to Main()
Console.WriteLine("Teensy Flash Console");
Console.WriteLine("Number of arguments in args = {0}\n", args.Length);
int argindex = 0;
string comPortStr = string.Empty;
foreach (var item in args)
{
Console.WriteLine(item);
if (item.Contains("COM"))
{
comPortStr = args[argindex];
}
argindex++;
}
string build_path = args[0];
string projectName = args[args.Length - 1];
projectName = projectName.Substring(0, projectName.Length - 4); //remove extension
build_path = build_path.Replace("\"", string.Empty).Trim();
string hexfilename = build_path + "\\" + projectName + ".hex";
Console.WriteLine("path = {0}", build_path);
Console.WriteLine("comport = {0}", comPortStr);
Console.WriteLine("build name = {0}", projectName);
Console.WriteLine("path to HEX file = {0}", hexfilename);
try
{
Stopwatch stopwatch = Stopwatch.StartNew();
string[] lines = System.IO.File.ReadAllLines(hexfilename);
foreach (string item in lines)
{
Console.WriteLine(item);
}
Console.WriteLine("this took " + stopwatch.ElapsedMilliseconds + " Msec");
}
catch (Exception)
{
throw;
}
Console.WriteLine("Opening Serial Port...");
try
{
SerialPort _serport = new SerialPort(comPortStr, 115200);
_serport.WriteTimeout = 1000;
_serport.WriteBufferSize = 20480;
_serport.Open();
_serport.DiscardOutBuffer();
_serport.DiscardInBuffer();
Thread.Sleep(100);
Console.WriteLine("Sending Trigger Character " + startCmdStr);
Console.WriteLine(startCmdStr);
_serport.Write(startCmdStr);
Console.WriteLine("Waiting for 'waiting' from Teensy...");
rcvStr = string.Empty;
aTimer = new System.Timers.Timer();
//aTimer.Interval = 5000;
aTimer.Interval = 25000;
aTimer.Elapsed += OnTimedEvent;
aTimer.Start();
while (!rcvStr.Contains("waiting") && !bTimedOut)
{
if (_serport.BytesToRead > 0)
{
rcvStr = _serport.ReadLine();
}
}
aTimer.Stop();
if (bTimedOut)
{
Console.WriteLine("Timed out waiting for 'waiting' response from Teensy");
}
else
{
//if we get to here, the Teensy is ready to receive HEX file contents
Console.WriteLine("Received " + rcvStr + " from Teensy");
Stopwatch stopwatch2 = Stopwatch.StartNew();
int numlines = 0;
string[] lines = System.IO.File.ReadAllLines(hexfilename);
foreach (string item in lines)
{
numlines++;
_serport.WriteLine(item);
}
Console.WriteLine("total lines = {0}, time = {1} mSec", numlines, stopwatch2.ElapsedMilliseconds);
//now we wait for Teensy to emit "hex file: xx lines xx bytes..." and then "enter xx to flash..."
aTimer.Start();
while (!rcvStr.Contains("hex file:") && !bTimedOut)
{
if (_serport.BytesToRead > 0)
{
rcvStr = _serport.ReadLine();
}
}
aTimer.Stop();
aTimer.Dispose();
if (bTimedOut)
{
Console.WriteLine("Timed out waiting for 'hex file' response from Teensy");
}
else
{
//extract number of lines from Teensy string, and compare with numlines.
//If they match, then send the number back to Teensy to complete the update.
//Otherwise, send '0' to abort
int colonIdx = rcvStr.IndexOf(':');
int lineIdx = rcvStr.IndexOf("lines");
string compareStr = rcvStr.Substring(colonIdx + 1, lineIdx - colonIdx - 1);
compareStr = compareStr.Trim();
int numTeensyLines = Convert.ToInt16(compareStr);
Console.WriteLine("sent {0} teensy replied {1}", numlines, numTeensyLines);
if (numTeensyLines == numlines)
{
Console.WriteLine("numlines {0} matches numTeensyLines {1} - send confirmation",
numlines, numTeensyLines);
_serport.WriteLine(compareStr);
}
}
}
}
catch (Exception)
{
throw;
}
try
{
}
catch (Exception)
{
throw;
}
}
static string chksum(string input)
{
int TwosComplement(string s)
{
if (s.Length % 2 != 0)
throw new FormatException(nameof(input));
var checksum = 0;
for (var i = 0; i < s.Length; i += 2)
{
var value = int.Parse(s.Substring(i, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
checksum = (checksum + value) & 0xFF;
}
return 256 - checksum & 0xFF;
}
//return string.Concat(":", input, " ", TwosComplement(input).ToString("X2"));
return TwosComplement(input).ToString("X2");
}
private static void SetTimer()
{
// Create a timer with a two second interval.
aTimer = new System.Timers.Timer(2000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
//aTimer.AutoReset = true;
aTimer.Enabled = true;
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
e.SignalTime);
bTimedOut = true;
}
}
}
The code that actually does the file transfer is:
Stopwatch stopwatch2 = Stopwatch.StartNew();
int numlines = 0;
string[] lines = System.IO.File.ReadAllLines(hexfilename);
foreach (string item in lines)
{
numlines++;
_serport.WriteLine(item);
}
Console.WriteLine("total lines = {0}, time = {1} mSec", numlines, stopwatch2.ElapsedMilliseconds);
When the '_serport.WriteLine(item);' line is commented out, the reported elapsed time is 0 mSec, as expected - so the 'ReadAllLines()' step isn't the problem.
Anyone have an idea why the 'WriteLine(item)' processing is so slow?
It depends on the structure of the method itself with the device, but I found on the same site an inquiry and means for it that help you
stackoverflow: console writeline slow
first sorry for my bad English
i want to make an order in my program when it come 12:00 AM in my PC
i try with this code
string Time = "00:00 AM";
while (true)
{
if (Time == DateTime.UtcNow.ToString("hh:mm tt"))
{
Console.ForegroundColor = ConsoleColor.Blue;
// Update Days -1 Where Service = 1
sqlCon.exec("update HelperSystem set Days = Days-1 where Service=1 and Days != 0");
Meldung("Updated HelperSystem Table Days -1");
// Update Service = 0 Where Days = 0
sqlCon.exec("update HelperSystem set Service = 0 where Days = 0");
Meldung("Updated HelperSystem Table Service = 0 Where Days = 0");
// Update Days -1 Where Service = 1
sqlCon.exec("update AutoEvent set Days = Days-1 where Service=1 and Days != 0");
Meldung("Updated AutoEvent Table Days -1");
// Update Service = 0 Where Days = 0
sqlCon.exec("update AutoEvent set Service = 0 where Days = 0");
Meldung("Updated AutoEvent Table Service = 0 Where Days = 0");
Console.ResetColor();
Thread.Sleep(120000);
}
Thread.Sleep(1);
}
and when i try with breakpoint return value 12:00 AM but didn't do anything
If you're trying to match times using a formatted string then you should ensure you're comparing against the same format. You should do something like this:
string timeFormat = "hh:mm tt";
string Time = (new DateTime(1970, 1, 1, 0, 0, 0)).ToString(timeFormat); //12:00 AM
while (true)
{
if (Time == DateTime.UtcNow.ToString(timeFormat))
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("Run.");
Console.ResetColor();
Thread.Sleep(120000);
}
Thread.Sleep(1);
}
However, this approach isn't great. You really have nothing ensuring that the process will actually execute at the right time of day to ensure equality works. In this case you'd be very unlucky as it only has to run once in the 60 seconds that the time text is correct, but there is no guarantee. You really should compare against a specific time.
This is better:
DateTime Time = DateTime.UtcNow.AddDays(1.0).Date;
while (true)
{
if (DateTime.UtcNow > Time)
{
Time = DateTime.UtcNow.AddDays(1.0).Date;
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("Run.");
Console.ResetColor();
}
Thread.Sleep(1);
}
There's no need for the messy Thread.Sleep(120000); in this code.
However, you still have the Thread.Sleep(1); call which is bad.
I'd suggest using a library that has been designed for this kind of thing. Try Microsoft's Reactive Framework (NuGet "System.Reactive") and then you can do this:
IDisposable subscription =
Observable
.Timer(DateTimeOffset.UtcNow.AddDays(1.0).Date, TimeSpan.FromDays(1.0))
.Subscribe(x =>
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("Run.");
Console.ResetColor();
});
When you're closing down your app just call subscription.Dispose(); and it'll cleanly stop.
Change string Time = "4:30 AM"; to "04:30 AM"
public static void Main()
{
string Time = "4:30 AM";
while (true)
{
Console.WriteLine("{0}",DateTime.UtcNow.ToString("hh:mm tt"));
// Prints 04:30 AM - so that is why it does not match 4:30 AM
if (Time == DateTime.UtcNow.ToString("hh:mm tt"))
{
Console.ForegroundColor = ConsoleColor.Blue;
// Update Days -1 Where Service = 1
Console.WriteLine("I am In");
Console.ResetColor();
}
Thread.Sleep(1000);
}
}
this is going to sound silly but for some reason this code seems to skip over the read. Or maybe it is going too fast? I'm trying to get a telnet response from the server and I get the first line response but nothing more. It doesn't matter how many readline() I put or if I try to sleep the thread. What can I do to get the whole response from the server to be printed? I think I narrowed the problem down to exiting. In the sample program if you hard code the exit, it immediately ends the loop and displays nothing. I tried sleeping the thread but that just seemed to stop everything.
Output:
220 server-12.tower-558.messagelabs.com ESMTP
Expected Output:
250-server-11.tower-555.messagelabs.com says EHLO to iphere
250-PIPELINING
250-8BITMIME
250-STARTTLS
My Code:
//Telnet Start
IPHostEntry hostInfo = Dns.Resolve(list[j]);
TelnetConnection tc = new TelnetConnection(hostInfo.AddressList[0].ToString(), 25);
string prompt = "a";
string consoleout = "";
// while connected
while (tc.IsConnected && prompt != "exit")
{
// display server output
prompt = "ehlo a.com";
tc.WriteLine(prompt);
//I've tried adding a 2 or 5 second thread sleep here and I still get the same result.
Console.Write(tc.Read());
prompt = "exit";
}
//Telnet End
TelnetConnection Class:
// minimalistic telnet implementation
// conceived by Tom Janssens on 2007/06/06 for codeproject
//
// http://www.corebvba.be
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
namespace MinimalisticTelnet
{
enum Verbs {
WILL = 251,
WONT = 252,
DO = 253,
DONT = 254,
IAC = 255
}
enum Options
{
SGA = 3
}
class TelnetConnection
{
TcpClient tcpSocket;
int TimeOutMs = 100;
public TelnetConnection(string Hostname, int Port)
{
try
{
tcpSocket = new TcpClient(Hostname, Port);
}
catch (SocketException e)
{
Console.Write(e);
}
}
public string Login(string Username,string Password,int LoginTimeOutMs)
{
int oldTimeOutMs = TimeOutMs;
TimeOutMs = LoginTimeOutMs;
string s = Read();
if (!s.TrimEnd().EndsWith(":"))
throw new Exception("Failed to connect : no login prompt");
WriteLine(Username);
s += Read();
if (!s.TrimEnd().EndsWith(":"))
throw new Exception("Failed to connect : no password prompt");
WriteLine(Password);
s += Read();
TimeOutMs = oldTimeOutMs;
return s;
}
public void WriteLine(string cmd)
{
Write(cmd + "\n");
}
public void Write(string cmd)
{
if (!tcpSocket.Connected) return;
byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF","\0xFF\0xFF"));
tcpSocket.GetStream().Write(buf, 0, buf.Length);
}
public string Read()
{
if (!tcpSocket.Connected) return null;
StringBuilder sb=new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
public bool IsConnected
{
get { return tcpSocket.Connected; }
}
void ParseTelnet(StringBuilder sb)
{
while (tcpSocket.Available > 0)
{
int input = tcpSocket.GetStream().ReadByte();
switch (input)
{
case -1 :
break;
case (int)Verbs.IAC:
// interpret as command
int inputverb = tcpSocket.GetStream().ReadByte();
if (inputverb == -1) break;
switch (inputverb)
{
case (int)Verbs.IAC:
//literal IAC = 255 escaped, so append char 255 to string
sb.Append(inputverb);
break;
case (int)Verbs.DO:
case (int)Verbs.DONT:
case (int)Verbs.WILL:
case (int)Verbs.WONT:
// reply to all commands with "WONT", unless it is SGA (suppres go ahead)
int inputoption = tcpSocket.GetStream().ReadByte();
if (inputoption == -1) break;
tcpSocket.GetStream().WriteByte((byte)Verbs.IAC);
if (inputoption == (int)Options.SGA )
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL:(byte)Verbs.DO);
else
tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT);
tcpSocket.GetStream().WriteByte((byte)inputoption);
break;
default:
break;
}
break;
default:
sb.Append( (char)input );
break;
}
}
}
}
}
UPDATE:
Using the following loop I was able to get it to work once, but only once it would not iterate through the count of j when I verified that j was 2. My output, expected output and function are below.
Function:
for (int j = 0; j < list.Count; j++)
{
//Telnet Start
Console.WriteLine("On round #" + j);
IPHostEntry hostInfo = Dns.Resolve(list[j]);
TelnetConnection tc = new TelnetConnection(hostInfo.AddressList[0].ToString(), 25);
string prompt = "a";
string consoleout = "";
// while connected
while (tc.IsConnected && prompt != "exit")
{
// display server output
Console.Write(tc.Read());
// send client input to server
prompt = "ehlo a.com";
tc.WriteLine(prompt);
// display server output
consoleout = tc.Read();
Console.Write(consoleout);
//send exit input to server
prompt = "exit";
tc.WriteLine(prompt);
Console.Write(tc.Read());
}
Console.WriteLine("**DISCONNECTED**");
//Telnet End
if (consoleout.IndexOf("STARTTLS")>-1)
{
if (j == 0)
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(domains[i], list[j], "Y", numberemployees, "Y");
else
dataGridView1.Rows.Add(domains[i], list[j], "Y", numberemployees, "N");
}
else
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(null, list[j], "Y", null, "Y");
else
dataGridView1.Rows.Add(null, list[j], "Y", null, "N");
}
}
else
{
if (j == 0)
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(domains[i], list[j], "N", numberemployees, "Y");
else
dataGridView1.Rows.Add(domains[i], list[j], "N", numberemployees, "N");
}
else
{
if (list[j].Contains(domains[i]))
dataGridView1.Rows.Add(null, list[j], "N", null, "Y");
else
dataGridView1.Rows.Add(null, list[j], "N", null, "N");
}
}
}
Output:
On round #0
True220 server-6.tower-95.messagelabs.com ESMTP
250-server-6.tower-95.messagelabs.com
250-STARTTLS
250-PIPELINING
250 8BITMIME
502 unimplemented (#5.5.1)
**DISCONNECTED**
On round #1
True220 server-14.tower-558.messagelabs.com ESMTP
**DISCONNECTED**
Expected Output:
On round #0
True220 server-6.tower-95.messagelabs.com ESMTP
250-server-6.tower-95.messagelabs.com
250-STARTTLS
250-PIPELINING
250 8BITMIME
502 unimplemented (#5.5.1)
**DISCONNECTED**
On round #1
True220 server-14.tower-558.messagelabs.com ESMTP
250-STARTTLS
250-PIPELINING
250 8BITMIME
**DISCONNECTED**
So for whatever reason, it simply won't do the second loop. But it also is starting to depend on the server. Some servers respond with a little information and some I get something like this, which makes it look like nothing happened at all.
On round #0
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #1
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #2
True220 SMTP Server Ready
**DISCONNECTED**
On round #3
True220 SMTP Server Ready
**DISCONNECTED**
On round #4
True220 SMTP Proxy Server Ready
**DISCONNECTED**
On round #5
True220 SMTP Proxy Server Ready
**DISCONNECTED**
Kyle,
It looks like you need to open the socket:
public string Read()
{
if (!tcpSocket.Connected)
{
throw new Exception("Socket is Closed.");
}
StringBuilder sb=new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
It isn't open, so it is returning.
[UPDATE]
Try looping through IPHostEntry hostInfo = Dns.Resolve(list[j]); before your //Telnet Start comment:
private const int PORT25 = 25; // I hate magic numbers
foreach (var item in list) {
var hostInfo = Dns.Resolve(item);
Console.WriteLine(hostInfo);
foreach (var address in hostInfo.AddressList) {
var tc = new TelnetConnection(address, PORT25);
Console.WriteLine("{0} TelnetConnection Connected: {1}", address, tc.IsConnected);
}
}
[Update 2]
This is really hard to debug, not knowing what all you are trying to connect to and what else is going on.
That said, let's try the following:
In your class MinimalisticTelnet, add this method:
public void Close() {
if (tcpSocket != null) {
tcpSocket.Close();
}
}
I don't see it anywhere else, and that could be causing some issues the next time you attempt your second connection.
Now, in your test code, add the new one line of code after the while loop:
while (tc.IsConnected && prompt != "exit") {
// display server output
Console.Write(tc.Read());
// send client input to server
prompt = "ehlo a.com";
tc.WriteLine(prompt);
// display server output
consoleout = tc.Read();
Console.Write(consoleout);
//send exit input to server
prompt = "exit";
tc.WriteLine(prompt);
Console.Write(tc.Read());
}
tc.Close();
Console.WriteLine("**DISCONNECTED**");
With any luck, the reason your 2nd connection was failing was because you still had an open connection.
I solve a so similar problem adding 3 reads, then 8 seconds timeout and finally reading again AND SHOWING IN MY RICHTEXBOX:
string r1 = conexion.Read();
string r2 = conexion.Read();
string r3 = conexion.Read();
System.Threading.Thread.Sleep(8000);
richtextbox.AppendText(r1 + r2 + r3);
I hope it can help you!
Tell me if it works to you.
Question: I want to search the subnet for all computers in it.
So I send a ping to all IP addresses in the subnet.
The problem is it works fine if I only scan 192.168.0.".
But if I scan 192.168..*", then I get an "Out of memory" exception.
Why ? Do I have to limit the threads, or is the problem the memory consumed by new ping which doesn't get destructed once finished, or do I need to call gc.collect() ?
static void Main(string[] args)
{
string strFromIP = "192.168.0.1";
string strToIP = "192.168.255.255";
Oyster.Math.IntX omiFromIP = 0;
Oyster.Math.IntX omiToIP = 0;
IsValidIP(strFromIP, ref omiFromIP);
IsValidIP(strToIP, ref omiToIP);
for (Oyster.Math.IntX omiThisIP = omiFromIP; omiThisIP <= omiToIP; ++omiThisIP)
{
Console.WriteLine(IPn2IPv4(omiThisIP));
System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiThisIP));
SendPingAsync(sniIPaddress);
}
Console.WriteLine(" --- Press any key to continue --- ");
Console.ReadKey();
} // Main
// http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
// http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
// http://www.kloth.net/services/iplocate.php
// http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
// http://stackoverflow.com/questions/1434342/ping-class-sendasync-help
public static void SendPingAsync(System.Net.IPAddress sniIPaddress)
{
int iTimeout = 5000;
System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();
System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
string data = "ABC";
byte[] dataBuffer = Encoding.ASCII.GetBytes(data);
parmPing.DontFragment = true;
parmPing.Ttl = 32;
myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
//waiter.WaitOne();
}
private static void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
{
System.Net.NetworkInformation.PingReply reply = e.Reply;
((System.Threading.AutoResetEvent)e.UserState).Set();
if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
{
Console.WriteLine("Address: {0}", reply.Address.ToString());
Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
}
}
According to this thread, System.Net.NetworkInformation.Ping seems to allocate one thread per async request, and "ping-sweeping a class-B network creates 100's of threads and eventually results in an out-of-memory error."
The workaround that person used was to write their own implementation using raw sockets. You don't have to do that in F#, of course, but there are a number of advantages in doing so.
First: Only start like 1000 pings the first time (in the loop in Main)
Second: Move the following parameters to Program class (member variables)
Oyster.Math.IntX omiFromIP = 0;
Oyster.Math.IntX omiToIP = 0;
Oyster.Math.IntX omiCurrentIp = 0;
object syncLock = new object();
Third: In AsyncPingCompleted do something like this in the bottom:
public void AsyncPingCompleted (bla bla bla)
{
//[..other code..]
lock (syncLock)
{
if (omiToIP < omiCurrentIp)
{
++omiCurrentIp;
System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(omiCurrentIp));
SendPingAsync(sniIPaddress);
}
}
}
Update with complete code example
public class Example
{
// Number of pings that can be pending at the same time
private const int InitalRequests = 10000;
// variables from your Main method
private Oyster.Math.IntX _omiFromIP = 0;
private Oyster.Math.IntX _omiToIP = 0;
private Oyster.Math.IntX _omiCurrentIp = 0;
// synchronoize so that two threads
// cannot ping the same IP.
private object _syncLock = new object();
static void Main(string[] args)
{
string strFromIP = "192.168.0.1";
string strToIP = "192.168.255.255";
IsValidIP(strFromIP, ref _omiFromIP);
IsValidIP(strToIP, ref _omiToIP);
for (_omiCurrentIp = _omiFromIP; _omiCurrentIp <= _omiFromIP + InitalRequests; ++_omiCurrentIp)
{
Console.WriteLine(IPn2IPv4(_omiCurrentIp));
System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
SendPingAsync(sniIPaddress);
}
Console.WriteLine(" --- Press any key to continue --- ");
Console.ReadKey();
} // Main
// http://pberblog.com/post/2009/07/21/Multithreaded-ping-sweeping-in-VBnet.aspx
// http://www.cyberciti.biz/faq/how-can-ipv6-address-used-with-webbrowser/#comments
// http://www.kloth.net/services/iplocate.php
// http://bytes.com/topic/php/answers/829679-convert-ipv4-ipv6
// http://stackoverflow.com/questions/1434342/ping-class-sendasync-help
public void SendPingAsync(System.Net.IPAddress sniIPaddress)
{
int iTimeout = 5000;
System.Net.NetworkInformation.Ping myPing = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingOptions parmPing = new System.Net.NetworkInformation.PingOptions();
System.Threading.AutoResetEvent waiter = new System.Threading.AutoResetEvent(false);
myPing.PingCompleted += new System.Net.NetworkInformation.PingCompletedEventHandler(AsyncPingCompleted);
string data = "ABC";
byte[] dataBuffer = Encoding.ASCII.GetBytes(data);
parmPing.DontFragment = true;
parmPing.Ttl = 32;
myPing.SendAsync(sniIPaddress, iTimeout, dataBuffer, parmPing, waiter);
//waiter.WaitOne();
}
private void AsyncPingCompleted(Object sender, System.Net.NetworkInformation.PingCompletedEventArgs e)
{
System.Net.NetworkInformation.PingReply reply = e.Reply;
((System.Threading.AutoResetEvent)e.UserState).Set();
if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
{
Console.WriteLine("Address: {0}", reply.Address.ToString());
Console.WriteLine("Roundtrip time: {0}", reply.RoundtripTime);
}
// Keep starting those async pings until all ips have been invoked.
lock (_syncLock)
{
if (_omiToIP < _omiCurrentIp)
{
++_omiCurrentIp;
System.Net.IPAddress sniIPaddress = System.Net.IPAddress.Parse(IPn2IPv4(_omiCurrentIp));
SendPingAsync(sniIPaddress);
}
}
}
}
I guess the problem is that you are spawning roughly 63K ping requests near-simultaneously. Without further memory profiling it is hard to say which parts consume the memory. You are working with network resources, which probably are limited. Throttling the number of active pings will ease the use of local resources, and also network traffic.
Again I would look into the Task Parallel Library, the Parallel.For construct combined with the Task<T> should make it easy for you.
Note: for .Net 3.5 users, there is hope.
I did something similar to this. The way I solved the problem on my project was to cast the ping instance to IDisposable:
(myPing as IDisposable).Dispose()
So get a list of say 254 ping instances running asynchronously (X.X.X.1/254) and keep track of when all of them have reported in. When they have, iterate through your list of ping instances, run the above code on each instance, and then dump the list.
Works like a charm.
pseudo-code
do
if pings_running > 100 then
sleep 100ms.
else
start ping
endif
loop while morepings
Finally... No ping requried at all...
http://www.codeproject.com/KB/cs/c__ip_scanner.aspx
All I needed to do is to make it thread-safe for debugging.
Changing Add to:
void Add( string m )
{
Invoke(new MethodInvoker(
delegate
{
add.Items.Add(m);
}));
//add.Items.Add( m );
}
Private Sub Add(m As String)
Invoke(New MethodInvoker(Function() Do
add.Items.Add(m)
End Function))
'add.Items.Add(m);'
End Sub
I need my application to ping an address I'll specify later on and just simply copy the Average Ping Time to a .Text of a Label.
Any help?
EDIT:
I found the solution in case anyone is interested:
Ping pingClass = new Ping();
PingReply pingReply = pingClass.Send("logon.chronic-domination.com");
label4.Text = (pingReply.RoundtripTime.ToString() + "ms");
Give a look the NetworkInformation.Ping class.
An example:
Usage:
PingTimeAverage("stackoverflow.com", 4);
Implementation:
public static double PingTimeAverage(string host, int echoNum)
{
long totalTime = 0;
int timeout = 120;
Ping pingSender = new Ping ();
for (int i = 0; i < echoNum; i++)
{
PingReply reply = pingSender.Send (host, timeout);
if (reply.Status == IPStatus.Success)
{
totalTime += reply.RoundtripTime;
}
}
return totalTime / echoNum;
}
Just as a sidenote to this. There is allready a project on sourceforge doing about that what you want. This has also included an implementation of ICMP (RFC 792)
Sourceforge Project