I am building a server selection screen and have a bunch of IP addresses that need to be pinged in order to know the remote hosts are reachable and to establish a TCP connection afterwards to request their data (like connected playerCount and MOTD, think of Minecraft Multiplayer Selection screen, you don't connect to the game, only requesting data from the endpoint).
The problem is that Ping is always unsuccessful, because the PingReply is null.
For generic use cases, I have wrapped the call with an event that gets fired when the Ping Reply is received back to the sender.
public static async void PingNET(string address, int timeOutMs, System.Action<PingReply> onCompleted)
{
await Task.Run(() => { //to continue unity main thread, start new thread
AutoResetEvent waiter = new AutoResetEvent(false);
var pingSender = new System.Net.NetworkInformation.Ping ();
// call handler when PingCompleted event is raised
pingSender.PingCompleted += (sender, e) =>
{
Debug.LogError ("Error and reply is always null! See: Error is null?: "
+ (e.Error == null) + " - Reply is null?: " + (e.Reply == null));
// If the operation was canceled, display a message to the user.
if (e.Cancelled) {
Debug.LogWarning("Ping canceled.");
}
// If an error occurred, display the exception to the user.
if (e.Error != null) {
Debug.LogError ("Ping failed:" + e.Error.ToString ());
}
if(onCompleted != null)
onCompleted(e.Reply);
};
// Send the ping asynchronously (uses internal new thread,
// so program does not freeze until reply received)
pingSender.SendAsync(address, timeOutMs, waiter);
});
}
Unity Test Runner Unit Test:
[Test]
public void PingTest(){
List<string> addresses = new List<string>(){
"127.0.0.1", "localhost", "www.stackoverflow.com", "www.google.com"
};
for (int i = 0; i < addresses.Count; i++)
{
Ping(addresses[i]);
}
}
private void Ping(string address){
NetworkUtils.PingNET(address, 10000, new System.Action<PingReply>((PingReply reply) => {
if (reply != null && reply.Status == IPStatus.Success)
{
Debug.Log($"Address: {reply.Address.ToString()}");
Debug.Log($"RoundTrip time: {reply.RoundtripTime}");
}
else //this gets called obviously
{
Debug.LogError("PING REPLY ERROR " + reply?.Status);
}
}));
}
Side Info: (Not directly tied to the question)
The reason why I use Ping in the first place is to prevent freeze (deadlock) on the client when the server goes offline while the connection is still tied to it and requesting data to the now unavailable remote host endpoint of the server. I could notify all connected clients that the server is shutting down, but this of course will not work when the server immediately lost connection to the internet.
So all in all, when the ping fails, it should handle in the UI logic to display the server is unreachable. Otherwise if it was successful, a TCP connection to the server is created to send a request. The server that listens on the same port sends a response with the data (playerCount, MOTD, etc.). After that, the client deseralizes the byte array to the datatypes and fires an event, which is subscribed by the UI to gather the deserealized data and display those.
The sending/receiving part with UI already works, just the ping not.
Is this process overcomplicated? Let me know if it could be simplified.
Any help is greatly appreciated.
Related
I discovered some problems by calling the "Bind(IPEndPoint)" method on a socket instance as soon as an adapter changes it's ip address.
Questions:
Why is the new ip not available for about the first 4 seconds after changing it?
If it is expected to behave like this, how to avoid to provocate exceptions while waiting to get the ip available finally?
Do i have to change the socket binding ip in order to receive incoming connections after network adapter gets a new ip?
For reproduction, I appended the code on bottom. Run it, and while running it, change the ip address of the one network adapter displayed in the first output line.
Short description:
I register on "NetworkChange.NetworkAddressChanged". Now I change my IP address using windows, which fires the NetworkAddressChanged event. There, the socket listening on the old local endpoint ip, will be disposed and replaced by a new socket listening on the new local endpoint ip. This seems to work, but with a little delay around 4 seconds. Within those 4 seconds, the method "Bind" throws an SocketException with SocketErrorCode = AddressNotAvailable. What is going wrong there?
Simplified Code:
class Program {
private static Socket socket;
private static IPEndPoint localEP;
private static NetworkInterface niEth;
static void Main(string[] args) {
localEP = new IPEndPoint(IPAddress.Any, 12345);
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
RefreshNetworkInterfaceIP();
while (true) Thread.Sleep(100);
}
private static void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) {
RefreshNetworkInterfaceIP();
}
private static void RefreshNetworkInterfaceIP() {
niEth = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(IsUsableInterface);
Console.WriteLine("Using \"{0}\" as interface!", niEth.Name);
IPAddress newIP = niEth.GetIPProperties().UnicastAddresses.FirstOrDefault(x => x.Address.AddressFamily == AddressFamily.InterNetwork).Address;
// It looks like the new IPAddress is properly set, but Bind still refuses to use this ip...
if (!localEP.Address.Equals(newIP)) {
UnbindSocket();
localEP.Address = newIP;
BindSocket();
}
}
private static bool IsUsableInterface(NetworkInterface ni) {
return ni != null
&& ni.OperationalStatus == OperationalStatus.Up
&& (
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet
|| ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
&& ni.GetIPProperties().UnicastAddresses.Any(x => x.Address.AddressFamily == AddressFamily.InterNetwork);
}
private static void BindSocket() {
if (socket != null) UnbindSocket();
socket = new Socket(localEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
do { // This is the waiting workaround.
try { socket.Bind(localEP); } catch (SocketException e) {
if (e.SocketErrorCode == SocketError.AddressNotAvailable) {
Console.WriteLine("The new IP is not ready, waiting a sec...");
Thread.Sleep(1000);
}
}
} while (!socket.IsBound);
socket.Listen(1000);
Console.WriteLine("Socket now listens on: {0}", socket.LocalEndPoint);
}
private static void UnbindSocket() {
if (socket == null) return;
try { socket.Dispose(); } catch (ObjectDisposedException) { }
socket = null;
}
}
This code snippet actually works fine, and thanks to "CurrPorts", socket is really binding and listening to new IP. Even after a call to GC after "socket = null", same behavior.
Note: The surrounding mechanism of this code snippet can now successfully handle following situations:
Network adapters: Reaction on adding/removing adapters of any kind, even the invisible ones (isatap, ...)
WiFi: On/Off by Airplane-Mode, discrete switch and software/driver activation/deactivation/reset
RJ45: Cable out/in, software/driver activation/deactivation/reset
In those cases, the mechanism around that socket knows what it is doing, it is even waiting on isatab and tap-adapters to get their addresses to use them simultaneously.
The only thing is: Windows Network Discovery, triggered by IP address change (tested IPv4 only), which shows up the new ip in "NetworkInterface" but refuses to use it. If IPv6 struggles the same way IPv4 does, I have to find a proper solution for 2 problems except the one I thought it was the only one :)
I am still working forward to get my Project done (some kind of "Network Service Abstraction Layer" for high reliability), but those kind of problems are really nasty to solve and work with...
I would be very happy if someone can point me to the right direction to either wait precisely for binding the socket or to avoid exceptions while trying to bind to that new ip.
Thanks in advance, GK
Overview of Problem:
I need to connect to an IRC Server. Once connected, the program will send a message to the channel, and a response will occur over multiple lines back. I need to read these lines and store in a variable for later use. A special character at the end of the message (]) will define the end of the message over multiple lines. Once we have received this character, the IRC session should disconnect and processing should continue.
Situation:
I am using the Smartirc4net library. Calling irc.Disconnect() takes about 40 seconds to disconnect the session. Once we've received the ] character, the session should be disconnected, Listen() should not be blocking, and the rest of the program should continue to run.
Research:
I have found this: smartirc4net listens forever, can't exit thread, and I think it might be the same issue, however, I am unsure of what I need to do to resolve the problem.
Code:
public class IrcCommunicator
{
public IrcClient irc = new IrcClient();
string data;
public string Data { get { return data; } }
// this method we will use to analyse queries (also known as private messages)
public void OnQueryMessage(object sender, IrcEventArgs e)
{
data += e.Data.Message;
if (e.Data.Message.Contains("]"))
{
irc.Disconnect(); //THIS TAKES 40 SECONDS!!!
}
}
public void RunCommand()
{
irc.OnQueryMessage += new IrcEventHandler(OnQueryMessage);
string[] serverlist;
serverlist = new string[] { "127.0.0.1" };
int port = 6667;
string channel = "#test";
try
{
irc.Connect(serverlist, port);
}
catch (ConnectionException e)
{
// something went wrong, the reason will be shown
System.Console.WriteLine("couldn't connect! Reason: " + e.Message);
}
try
{
// here we logon and register our nickname and so on
irc.Login("test", "test");
// join the channel
irc.RfcJoin(channel);
irc.SendMessage(SendType.Message, "test", "!query");
// here we tell the IRC API to go into a receive mode, all events
// will be triggered by _this_ thread (main thread in this case)
// Listen() blocks by default, you can also use ListenOnce() if you
// need that does one IRC operation and then returns, so you need then
// an own loop
irc.Listen();
// when Listen() returns our IRC session is over, to be sure we call
// disconnect manually
irc.Disconnect();
}
catch (Exception e)
{
// this should not happen by just in case we handle it nicely
System.Console.WriteLine("Error occurred! Message: " + e.Message);
System.Console.WriteLine("Exception: " + e.StackTrace);
}
}
}
IrcBot bot = new IrcBot();
bot.RunCommand();
ViewBag.IRC = bot.Data;
As you can see, once this
Thank you for your time to look at this code and read my problem description. If you have any thoughts, or other suggestions, please let me know.
Mike
I was able to successfully disconnect straight away by calling RfcQuit() within OnQueryMessage(), before irc.Disconnect();
When using Ping in correlation with PingReply to check the status of an IP Address and it's ports for and imported text list how do you launch a code to skip the current one and move onto the next one?
PingReply reply = ping.Send("IP", "PORT");
Specifically
PingReply reply = ping.Send("174.69.75.251", "41968");
There is no response at all, it just freezes the application so you cant check the reply status if its successful.
Going to a list of proxies I want to check if they're valid and able to be connected to a webBrowser1 control so I have the following code to send the request for the IP Address and Port to check if it will accept connections.
This is the whole code for the loop and everything, I have added what has been suggested by two people and excluded the TCPClient one with /* */ heres the code for the button:
private void button2_Click(object sender, EventArgs e)
{
numberProx = Convert.ToInt32(textBox1.Lines.Length.ToString());
proxyList = textBox1.Text.Split(new Char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
while (i < numberProx)
{
string currentProxy = proxyList[i++].ToString();
try
{/*
TcpClient reply2 = new TcpClient();
reply2.ConnectAsync(currentProxy.Split(':')[0],
Convert.ToInt32(currentProxy.Split(':')[1]));
if (reply2.Connected)
{
textBox2.AppendText(currentProxy + "\n");
}
else
{
textBox3.AppendText(currentProxy + "\n");
}*/
//PingReply reply = proxy.Send(currentProxy.Split(':')[0], Convert.ToInt32(currentProxy.Split(':')[1]));
PingReply reply = await proxy.SendPingAsync("174.69.75.251", 5000);
if (reply.Status == IPStatus.Success)
{
textBox2.AppendText(currentProxy + "\n");
}
else if (reply.Status == IPStatus.TimedOut)
{
}
else if (reply.RoundtripTime >= 5000)
{
textBox3.AppendText(currentProxy + "\n");
}
else
{
textBox3.AppendText(currentProxy + "\n");
}
}
catch (PingException ex)
{
MessageBox.Show("Error: " + ex.Message);
}
}
}
That is everything including the loop and incremented integer to match the number of proxies in the string[] called proxyList.
What I want to do is see if the proxy is capable of working in a webBrowser control without the form/UI freezing.
A ping request can't test an applicative PORT. For this, you have telnet.
The parameters taken by 'ping.Send' are:
ping.Send('IP_ADDRESS', 'TIMEOUT');
Like it is said in the MSDN Documentation
You could specify a timeout using the overload of Ping.Send that accepts one. This takes the number of milliseconds to wait before timing out.
If you are in a UI application and this is causing your UI thread to freeze you could use the asynchronous method and await the result. This would allow your UI to remain responsive while the request is being sent.
I have an electronic device which is connected to the computer over LAN. I have it's SDK to communicate with it.
Before communicating with the device it must be connected and registered with code provided in SDK. That's all fine.
My win application connects and registers the machine when the application starts up, but the problem is if the electronic device is SWITCHED OFF and then SWITCHED ON, the application must again connect and register the device.
So the question is how to continuously monitor if the device is connected over IP (something like PINGING it via C# code).
So one problem while constantly PINGing the device is that I must run it on separate thread, so that it doesn't affect my actual application. Another good solution would be if I get some piece code which fires event when the device is disconnected over IP.
Thanks for help.
Regards,
EDIT 1: Some piece of code which I use to connect using SDK
bool connected;
connected = axCZKEM1.Connect_Net("192.168.1.201", "4370");
bool registered;
registered = axCZKEM1.RegEvent(1, 65535);
EDIT 2: I am trying the method from answer by sa_ddam213, but I never get a ping failure. Also, it makes my PC run slow. What error am I making ?
while (true)
{
Ping ping = new Ping();
ping.PingCompleted += (sender, e) =>
{
if(e.Reply.Status != IPStatus.Success)
{
connected=false;
registered=false;
while(connected && registered)
{
connected = axCZKEM1.Connect_Net("192.168.1.201", 4370);
registered = axCZKEM1.RegEvent(1, 65535);
}
}
};
ping.Send("192.168.1.201", 3000);
}
You could Ping using c#,
PingReply reply = new Ping().Send("127.0.0.1");
if (reply.Status == IPStatus.Success)
{
// yay
}
Async method:
Ping ping = new Ping();
ping.PingCompleted += (sender, e) =>
{
if (e.Reply.Status == IPStatus.Success)
{
// yay
}
};
ping.SendAsync("127.0.0.1", 3000, null);
I have a server/client type app, Wireshark shows that the client has sent a packet to the server, the server had given the expected response but shows a ICMP Destination port unreachable error.
I'm using a function that was on the MDSN website which has worked for me before.
EDIT: To update I have checked that the packet is being sent after the phone has started listening, I have tried other ports. There is no socket exception so i'm just looking for the best way to go about debugging network errors.
Any ideas?
public string Receive()
{
string response = "Operation Timeout";
// We are receiving over an established socket connection
if (udpSocket != null)
{
// Create SocketAsyncEventArgs context object
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = new DnsEndPoint(SERVER, RECIVEPORT);
// Setup the buffer to receive the data
socketEventArg.SetBuffer(new Byte[MAX_BUFFER_SIZE], 0, MAX_BUFFER_SIZE);
// Inline event handler for the Completed event.
// Note: This even handler was implemented inline in order to make this method self-contained.
socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
// Retrieve the data from the buffer
response = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
response = response.Trim('\0');
}
else
{
response = e.SocketError.ToString();
}
_clientDone.Set();
});
// Sets the state of the event to nonsignaled, causing threads to block
_clientDone.Reset();
// Make an asynchronous Receive request over the socket
Debug.WriteLine("Listening now:" + DateTime.Now.Second + ":" + DateTime.Now.Millisecond);
try
{
Debug.WriteLine("No socket exception");
udpSocket.ReceiveFromAsync(socketEventArg);
}
catch (SocketException e)
{
Debug.WriteLine(e.SocketErrorCode);
}
// Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
// If no response comes back within this time then proceed
_clientDone.WaitOne(TIMEOUT_MILLISECONDS);
}
else
{
response = "Socket is not initialized";
}
return response;
}
"ICMP Destination port unreachable" means that there was no application bound to the port you were sending to. Make sure that your sendto() is targeting to correct IP address and port number. Also check that your listener is calling bind() on INADDR_ANY and the correct port. A common mistake is to forget to convert the port number to network byte order (big-endian). See htons().