C# IPv4/IPv6 agnostic socket listener - c#

I'm trying to to figure out how to create a protocol agnostic socket listener in C# - it should grab IPv4 and IPv6 requests. Everything I can find on Google seems to be C. Attempting something similar to what I saw for C, I tried the following code:
/*Socket*/ m_sock = null;
/*IPAddress*/ m_addr = null;
/*int*/ m_port = port; /*port passed to function*/
/*int*/ m_listenqueue = listen_queue_size; /*also passed to function, number of pending requests to allow before busy*/
IPAddress[] addrs = Dns.GetHostEntry("localhost").AddressList;
if(family == null) m_addr = addrs[0];
else
{
foreach(IPAddress ia in addrs)
{
if(ia.AddressFamily == family) /*desired address family also passed as an argument*/
{
m_addr = ia;
break;
}
}
}
if(m_addr == null) throw new Exception(this.GetType().ToString() + ".#CONSTRUCTOR#: Listener Initailization Error, couldn't get a host entry for 'localhost' with an address family of " + family.ToString());
m_sock = new Socket(m_addr.AddressFamily, SocketType.Stream, ProtocolType.IP);
/*START "AGNOSTICATION LOGIC"... Tried here...*/
if(m_addr.AddressFamily == AddressFamily.InterNetworkV6) //allow IP4 compatibility
{
m_sock.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.AcceptConnection, true);
/*fails*/ m_sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AcceptConnection, true);
/*fails*/ m_sock.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AcceptConnection, true);
}
/*END "AGNOSTICATION LOGIC" */
IPEndPoint _endpoint = new IPEndPoint(m_addr, m_port);
m_sock.Bind(_endpoint);
/*... tried here*/
m_sock.Listen(m_listenqueue);
/*... and tried here*/
I've tried the logic at the three places marked, and regardless of where I put it, the two listed lines will throw an invalid argument exception.
Can anyone recommend to me how I should make a socket that will listen to both IPv4/IPv6?

You can use sock.SetSockOption(SocketOptionLevel.IPv6, SocketOptionName.IPV6Only, 0); to set the socket to allow connections with other protocols than IPv6. It will work from Vista onwards.
SocketOptionName documentation

Related

How to get information from a remote UDP socket? C# & IPv4

I have hardware that sends information to the address 192.168.0.255 at approximately 5 second intervals (In the following image, the Wireshark software showing the device with IP address 192.168.0.241 sending the message "Hallo" to the address 192.168.0.255 on port 7000):
On the other hand, I have a desktop app made in C # that tries to read that information as follows:
int PORT = 7000;
udpClient = new UdpClient();
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, PORT));
private async Task<string> getData()
{
try
{
var from = new IPEndPoint(0, 0);
while (true)
{
var recvBuffer = udpClient.Receive(ref from);
string result= Encoding.UTF8.GetString(recvBuffer);
if (result != null && resultado.Length > 0)
{
return result;
}
}
}
...
}
It doesn't work (udpClient.Receive never returns information, it is similar to that there is no socket information yet), but if I open a software tool from my PC that allows me to write information to a UDP scoket, the code works wonderfully (udpClient.Receive captures the sent information.)
Any suggestions or comments?
UdpClient client;
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, PORT);
private void Connect()
{
client = new UdpClient(7000);
client.Connect(endPoint);
client.BeginReceive(ReceiveCallback, null);
}
private void ReceiveCallback(IAsyncResult _result)
{
try
{
byte[] _data = client.EndReceive(_result, ref endPoint);
socket.BeginReceive(ReceiveCallback, null);
if (_data.Length < 4)
{
//disconnected
return;
}
//now data is anything received and if you want to view it as a string do:
string result = Encoding.Default.GetString(data);
//you can also convert it to other things like ints, float, etc
}
catch
{
//disconnected
}
}
Thanks to #MarkusSafar's suggestion, I put the desktop app on another PC and from there it manages to capture the hardware message. I'm not sure why it doesn't work on my PC, but the next step is to test this same code for an app developed for Android (Xamarin), I hope it works there too.

Receiving UDP Multicast Messages on a Multihomed Windows 10 Machine

I have a C# .NET client which listens for UDP multicast messages. I need to receive messages on a single network interface. Sometimes I will not see the messages being received. When I disable the other interfaces, it works.
I attempted to set socket options to a specific interface using code from similar questions on this site, however, I'm unsure if this only affects sending multicast messages and not receiving them?
After my research, I see that the routing table causes this behavior and one solution is to change the routing table, but I prefer not to go that route.
Is it better to join the multicast group on all the interfaces? How would I do that using a UdpClient.
This is the code used to setup my UdpClient:
Getting the interfaces:
public static IEnumerable<NetworkInterface> GetAvailableMulticastInterfaces()
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
List<NetworkInterface> availableInterfaces = new List<NetworkInterface>();
foreach (NetworkInterface adapter in nics)
{
IPInterfaceProperties ip_properties = adapter.GetIPProperties();
if (!adapter.GetIPProperties().MulticastAddresses.Any())
continue; // most of VPN adapters will be skipped
if (!adapter.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != adapter.OperationalStatus)
continue; // this adapter is off or not connected
IPv4InterfaceProperties p = adapter.GetIPProperties().GetIPv4Properties();
if (null == p)
continue; // IPv4 is not configured on this adapter
availableInterfaces.Add(adapter);
}
return availableInterfaces;
}
Setting the Interface:
NetworkInterface networkInterface = Common.Utilities.Network.GetAvailableMulticastInterfaces().Where(nic => nic.Id == attributes.SelectedNetworkInterfaceId).FirstOrDefault();
Getting the Interface Index:
networkInterfaceIndex = (int)IPAddress.HostToNetworkOrder(networkInterface.GetIPProperties().GetIPv4Properties().Index);
Binding the UdpClient:
public void Listen(string multicastGroupAddress, int port, int? networkInterfaceIndex = null)
{
IpAddress = multicastGroupAddress;
Port = port;
_multicastEndPoint = new IPEndPoint(IPAddress.Parse(multicastGroupAddress), port);
Listener = new UdpClient();
Listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
if (networkInterfaceIndex != null)
Listener.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, networkInterfaceIndex.Value);
var localEndpoint = new IPEndPoint(IPAddress.Any, port);
Listener.Client.Bind(localEndpoint);
Listener.JoinMulticastGroup(IPAddress.Parse(multicastGroupAddress));
while (true)
Process();
}
Ok, so I came back to this and found the right operation to use. One must join on multiple interfaces using
var availableMulticastNics = Utils.GetAvailableMulticastInterfaces();
var listener = new UdpClient();
listener.Client.Bind(new IPEndPoint(IPAddress.Any, endpoint.Port));
foreach (var ni in availableMulticastNics)
{
listener.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastEndPoint.Address, ni.GetIPProperties().GetIPv4Properties().Index));
}
// Ready to read socket
Instead of listener.JoinMulticastGroup(ni.GetIPProperties().GetIPv4Properties().Index, multicastEndPoint.Address). Otherwise, I get a SocketException that tells me invalid argument, which someone can shed some light on if they can.

How to properly change a static IP address using the Microframework?

I have my Netduino Plus 2 go out to a web service to look up some values that I would like it to use in my project. One of the values that I have the Netduino check is its preferred IP address. If the Netduino has a different IPAddress than its preferred, I want to change it.
I have a method in my project called BindIPAddress (below) that takes a string.
I am getting a SocketException with a code of 10022 for invalid argument. This happens when I call this.Socket.Bind. My class has a property called Socket to hold the Socket value. Is it because my socket already has an endpoint ? I tried adding this.Socket = null and then this.Socket = new (....... thinking we need a new socket to work with, but this returned the same error.
Please advise how I can change my IP address from one static IP address to another.
public void BindIPAddress(string strIPAddress)
{
try
{
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticIP(strIPAddress, "255.255.240.0", "10.0.0.1");
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].EnableStaticDns(new string[] { "10.0.0.2", "10.0.0.3" });
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(strIPAddress), 80);
this.Socket.Bind(ep);
this.IpAddress = strIPAddress;
}
catch(SocketException exc)
{
Debug.Print(exc.Message);
Debug.Print(exc.ErrorCode.ToString());
}
catch(Exception ex)
{
Debug.Print(ex.Message);
}
//Debug.Print(ep.Address.ToString());
}
There may be 2 possible solutions to this problem. The first one is, you can programatically set the preffered IP addresses as the way you have tried to do so and the second one is, you can use MFDeploy tool, which comes with .NET Micro Framework SDK bundle that allows you to set your embedded device network configuration statically before running your application on it.
1) Since you have not provided rest of the code, here's a proper way to bind your socket to an EndPoint (actually, I would not design that class and binding function as the way you posted here, but just wanted to underline the missing parts of your code):
public void BindIPAddress(string strIPAddr)
{
Socket sock = null;
IPEndPoint ipe = null;
NetworkInterface[] ni = null;
try
{
ipe = new IPEndPoint(IPAddress.Parse(strIPAddr), 80);
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // Assuming the WebService is connection oriented (TCP)
// sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // if it broadcasts UDP packets, use this line (UDP)
ni = NetworkInterface.GetAllNetworkInterfaces();
if (ni != null && ni.Length > 0)
{
ni[0].EnableStaticIP(strIPAddr, "255.255.240.0", "10.0.0.1");
ni[0].EnableStaticDns(new string[2] { "10.0.0.2", "10.0.0.3" });
sock.Bind(ipe);
this.Socket = sock;
this.IpAddress = strIPAddr;
}
else
throw new Exception("Network interface could not be retrieved successfully!");
}
catch(Exception ex)
{
Debug.Print(ex.Message);
}
}
2) Or without programming, just by using MFDeploy tool you can set the preferred IP addresses after plugging your embedded device into your PC and following the path below:
MFDeploy > Target > Configuration > Network
then enter the preferred IP addresses. That's simply all.

TcpClient.Connected Always True [duplicate]

I'm playing around with the TcpClient and I'm trying to figure out how to make the Connected property say false when a connection is dropped.
I tried doing
NetworkStream ns = client.GetStream();
ns.Write(new byte[1], 0, 0);
But it still will not show me if the TcpClient is disconnected. How would you go about this using a TcpClient?
I wouldn't recommend you to try write just for testing the socket. And don't relay on .NET's Connected property either.
If you want to know if the remote end point is still active, you can use TcpConnectionInformation:
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
if (tcpConnections != null && tcpConnections.Length > 0)
{
TcpState stateOfConnection = tcpConnections.First().State;
if (stateOfConnection == TcpState.Established)
{
// Connection is OK
}
else
{
// No active tcp Connection to hostName:port
}
}
client.Close();
See Also:
TcpConnectionInformation on MSDN
IPGlobalProperties on MSDN
Description of TcpState states
Netstat on Wikipedia
And here it is as an extension method on TcpClient.
public static TcpState GetState(this TcpClient tcpClient)
{
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint));
return foo != null ? foo.State : TcpState.Unknown;
}
As far as I know/remember there is no way to test if a socket is connected other than reading or writing to it.
I haven't used the TcpClient at all but the Socket class will return 0 from a call to Read if the remote end has been shutdown gracefully.
If the remote end doesn't shutdown gracefully [I think] you get a timeout exception, can't remember the type sorry.
Using code like 'if(socket.Connected) { socket.Write(...) } creates a race condition. You're better off just calling socket.Write and handling the exceptions and/or disconnections.
The solution of Peter Wone and uriel is very nice. But you also need to check on the Remote Endpoint, since you can have multiple open connections to your Local Endpoint.
public static TcpState GetState(this TcpClient tcpClient)
{
var foo = IPGlobalProperties.GetIPGlobalProperties()
.GetActiveTcpConnections()
.SingleOrDefault(x => x.LocalEndPoint.Equals(tcpClient.Client.LocalEndPoint)
&& x.RemoteEndPoint.Equals(tcpClient.Client.RemoteEndPoint)
);
return foo != null ? foo.State : TcpState.Unknown;
}
I have created this function and working for me to check if client is still connected with server.
/// <summary>
/// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
/// </summary>
/// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
public bool isClientConnected()
{
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
foreach (TcpConnectionInformation c in tcpConnections)
{
TcpState stateOfConnection = c.State;
if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
{
if (stateOfConnection == TcpState.Established)
{
return true;
}
else
{
return false;
}
}
}
return false;
}
#uriel's answer works great for me, but I needed to code it in C++/CLI, which was not entirely trivial. Here is the (roughly equivalent) C++/CLI code, with a few robustness checks added in for good measure.
using namespace System::Net::Sockets;
using namespace System::Net::NetworkInformation;
TcpState GetTcpConnectionState(TcpClient ^ tcpClient)
{
TcpState tcpState = TcpState::Unknown;
if (tcpClient != nullptr)
{
// Get all active TCP connections
IPGlobalProperties ^ ipProperties = IPGlobalProperties::GetIPGlobalProperties();
array<TcpConnectionInformation^> ^ tcpConnections = ipProperties->GetActiveTcpConnections();
if ((tcpConnections != nullptr) && (tcpConnections->Length > 0))
{
// Get the end points of the TCP connection in question
EndPoint ^ localEndPoint = tcpClient->Client->LocalEndPoint;
EndPoint ^ remoteEndPoint = tcpClient->Client->RemoteEndPoint;
// Run through all active TCP connections to locate TCP connection in question
for (int i = 0; i < tcpConnections->Length; i++)
{
if ((tcpConnections[i]->LocalEndPoint->Equals(localEndPoint)) && (tcpConnections[i]->RemoteEndPoint->Equals(remoteEndPoint)))
{
// Found active TCP connection in question
tcpState = tcpConnections[i]->State;
break;
}
}
}
}
return tcpState;
}
bool TcpConnected(TcpClient ^ tcpClient)
{
bool bTcpConnected = false;
if (tcpClient != nullptr)
{
if (GetTcpConnectionState(tcpClient) == TcpState::Established)
{
bTcpConnected = true;
}
}
return bTcpConnected;
}
Hopefully this will help somebody.
As of 2019, in a cross-platform and async environment, I use the code below to continuosly check that the TCP channel is open. This check fires e.g. if the ethernet cable is pulled on my Windows machine, or if the Wifi is disabled on my Android device.
private async Task TestConnectionLoop()
{
byte[] buffer = new byte[1];
ArraySegment<byte> arraySegment = new ArraySegment<byte>(buffer, 0, 0);
SocketFlags flags = SocketFlags.None;
while (!_cancellationSource.Token.IsCancellationRequested)
{
try
{
await _soc.SendAsync(arraySegment, flags);
await Task.Delay(500);
}
catch (Exception e)
{
_cancellationSource.Cancel();
// Others can listen to the Cancellation Token or you
// can do other actions here
}
}
}
Please note that I have found GSF.Communication wrapper for System.Net.Sockets.TcpClient to be helpful because it has a CurrentState property that indicates whether the socket is open/connected or closed/disconnected. You can find details on the NuGet package here:
https://github.com/GridProtectionAlliance/gsf
Here is how you could setup a simple TCP socket and test whether it is connected:
GSF.Communication.TcpClient tcpClient;
void TestTcpConnectivity()
{
tcpClient = new GSF.Communication.TcpClient();
string myTCPServer = "localhost";
string myTCPport = "8080";
tcpClient.MaxConnectionAttempts = 5;
tcpClient.ConnectionAttempt += s_client_ConnectionAttempt;
tcpClient.ReceiveDataComplete += s_client_ReceiveDataComplete;
tcpClient.ConnectionException += s_client_ConnectionException;
tcpClient.ConnectionEstablished += s_client_ConnectionEstablished;
tcpClient.ConnectionTerminated += s_client_ConnectionTerminated;
tcpClient.ConnectionString = "Server=" + myTCPServer + ":" + myTCPport;
tcpClient.Initialize();
tcpClient.Connect();
Thread.Sleep(250);
if (tcpClient.CurrentState == ClientState.Connected)
{
Debug.WriteLine("Socket is connected");
// Do more stuff
}
else if (tcpClient.CurrentState == ClientState.Disconnected)
{
Debug.WriteLine(#"Socket didn't connect");
// Do other stuff or try again to connect
}
}
void s_client_ConnectionAttempt(object sender, EventArgs e)
{
Debug.WriteLine("Client is connecting to server.");
}
void s_client_ConnectionException(object sender, EventArgs e)
{
Debug.WriteLine("Client exception - {0}.", e.Argument.Message);
}
void s_client_ConnectionEstablished(object sender, EventArgs e)
{
Debug.WriteLine("Client connected to server.");
}
void s_client_ConnectionTerminated(object sender, EventArgs e)
{
Debug.WriteLine("Client disconnected from server.");
}
void s_client_ReceiveDataComplete(object sender, GSF.EventArgs<byte[], int> e)
{
Debug.WriteLine(string.Format("Received data - {0}.", tcpClient.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
}
I recommend the code from the answer of the user 'Uriel' above. His code in principle works great:
TcpClient client = new TcpClient(host, port);
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections().Where(x => x.LocalEndPoint.Equals(client.Client.LocalEndPoint) && x.RemoteEndPoint.Equals(client.Client.RemoteEndPoint)).ToArray();
but it has a bug:
IPEndPoint.Equals() is used here to search the retrieved list of TCP connections for the one connection that has the same endpoints as the socket TcpClient.Client of the used TCP client.
The idea and concept are fine, but in real life may fail because of the coexistence of IPv4 and IPv6: Current operating systems like Windows 10 support IPv4 and IPv6, and sockets may be created with IPv6 addresses even if addresses like "100.111.1.251" in the IPv4 format were configured:
// Creation of TCP client:
m_tcpClient = new TcpClient ();
m_tcpClient.Connect ("100.111.1.251", 54321);
// Query of the local and remote IP endpoints in Visual Studio Immediate Window:
?m_tcpClient.Client.LocalEndPoint
{[::ffff:100.111.1.254]:55412}
Address: {::ffff:100.111.1.254}
AddressFamily: InterNetworkV6
Port: 55412
?m_tcpClient.Client.RemoteEndPoint
{[::ffff:100.111.1.251]:54321}
Address: {::ffff:100.111.1.251}
AddressFamily: InterNetworkV6
Port: 54321
// Query of the addresses of the local and remote IP endpoints in Visual Studio Immediate Window:
?((IPEndPoint)m_tcpClient.Client.LocalEndPoint).Address
{::ffff:100.111.1.254}
Address: '((IPEndPoint)m_tcpClient.Client.LocalEndPoint).Address.Address' threw an exception of type 'System.Net.Sockets.SocketException'
AddressFamily: InterNetworkV6
IsIPv4MappedToIPv6: true
IsIPv6LinkLocal: false
IsIPv6Multicast: false
IsIPv6SiteLocal: false
IsIPv6Teredo: false
ScopeId: 0
?((IPEndPoint)m_tcpClient.Client.RemoteEndPoint).Address
{::ffff:100.111.1.251}
Address: '((IPEndPoint)m_tcpClient.Client.RemoteEndPoint).Address.Address' threw an exception of type 'System.Net.Sockets.SocketException'
AddressFamily: InterNetworkV6
IsIPv4MappedToIPv6: true
IsIPv6LinkLocal: false
IsIPv6Multicast: false
IsIPv6SiteLocal: false
IsIPv6Teredo: false
ScopeId: 0
AddressFamily: InterNetworkV6 and IsIPv4MappedToIPv6: true indicate that the IP address in the local IP endpoint is an IPv6 address, although an IPv4 address was used to establish the connection. This obviously is because the socket is created in "dual-mode" or as "dual-stack":
https://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses
https://learn.microsoft.com/en-us/dotnet/api/system.net.ipaddress.isipv4mappedtoipv6
https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
https://www.ibm.com/docs/en/zos/2.2.0?topic=addresses-ipv4-mapped-ipv6
IPGlobalProperties.GetActiveTcpConnections() on the other side seems to always return IPEndPoint objects with IPv4 addresses:
?IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections()[48].LocalEndPoint
{100.111.1.254:55412}
Address: {100.111.1.254}
AddressFamily: InterNetwork
Port: 55412
The consequence is that IPEndPoint.Equals() may return false even if two compared EndPoint objects refer to the same IP endpoint.
The solution for this problem is writing your own Equals() method, like:
public static class EndPointHelper
{
private static readonly AddressFamily[] addressFamilies =
{
AddressFamily.InterNetwork,
AddressFamily.InterNetworkV6
};
public static bool Equals (EndPoint? endPoint1, EndPoint? endPoint2)
{
if (endPoint1 is IPEndPoint ipEndPoint1 &&
endPoint2 is IPEndPoint ipEndPoint2)
{
if (ipEndPoint1.AddressFamily != ipEndPoint2.AddressFamily &&
addressFamilies.Contains(ipEndPoint1.AddressFamily) &&
addressFamilies.Contains(ipEndPoint2.AddressFamily))
{
var ipAddress1AsV6 = ipEndPoint1.Address.MapToIPv6();
var ipAddress2AsV6 = ipEndPoint2.Address.MapToIPv6();
return ipAddress1AsV6.Equals(ipAddress2AsV6)
&& ipEndPoint1.Port.Equals(ipEndPoint2.Port);
}
}
return object.Equals (i_endPoint1, i_endPoint2);
}
}
Furthermore, there is a bug in .NET 5, which makes the whole solution above unusable: IPGlobalProperties.GetActiveTcpConnections() has a memory leak (see https://github.com/dotnet/runtime/issues/64735), which will not be fixed in .NET 5 any more, because it has run out of support. The bug is not present in .NET 6. If you are tied to .NET 5, you will have to work around it by remembering the connection state yourself in a local variable (e.g. EnumState m_cachedState). Set this variable after each related operation, e.g. after a Connect() you would have to set it to EnumState.Connected.
This method of course will not detect when a connection was closed by the other side, so you have to cyclically check if the connection was closed, using this code:
var socket = m_tcpClient.Client;
bool state = socket.Poll (100, SelectMode.SelectRead);
int available = socket.Available;
return state && available == 0 // Condition for externally closed connection. The external close will not be recognized until all received data has been read.
? EnumState.Idle
: m_cachedState;
Try this, it works for me
private void timer1_Tick(object sender, EventArgs e)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
if (!client.Connected) sConnected = false;
else
{
byte[] b = new byte[1];
try
{
if (client.Client.Receive(b, SocketFlags.Peek) == 0)
{
// Client disconnected
sConnected = false;
}
}
catch { sConnected = false; }
}
}
if (!sConnected)
{
//--Basically what you want to do afterwards
timer1.Stop();
client.Close();
ReConnect();
}
}
i used Timer because, I wanted to check connection state at regular interval
and not in a LOOP with Listening code [I felt it was slowing the sending-recieving process]
In my case, I was sending some command to a server (running in a virtual machine on the same computer) and waiting for the response. However, if the server stopped unexpectedly while waiting, I did not get any notification. I tried the possibilities proposed by the other posters, but neither did work (it always said that the server is still connected). For me, the only thing that is working is to write 0 bytes to the stream:
var client = new TcpClient();
//... open the client
var stream = client.GetStream();
//... send something to the client
byte[] empty = { 0 };
//wait for response from server
while (client.Available == 0)
{
//throws a SocketException if the connection is closed by the server
stream.Write(empty, 0, 0);
Thread.Sleep(10);
}

UDP: Read data from all network interfaces

I've the following code to read multicast message coming from the network, for a specified IP+Port
private static void ReceiveMessages(int port, string ip, CancellationToken token)
{
Task.Factory.StartNew(() =>
{
using (var mUdpClientReceiver = new UdpClient())
{
var mReceivingEndPoint = new IPEndPoint(IPAddress.Any, port);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
while (!token.IsCancellationRequested)
{
byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
Console.WriteLine("Message received from {0} ",mReceivingEndPoint);
}
}
});
}
I've two network adapter from which I've data coming on this multicast ip+port(confirmed by two instances of wireshark monitoring each network adapter). I see on wireshark a lot of traffic coming on those port+Ip) for both network cards.
The problem is that on my console, I only see messages coming from one network card.
I double checked with netstat, I don't have any other software listening on my port:
So why am I getting traffic from only one of my two network cards?
EDIT:
I even tried the following:
private static void ReceiveMessages(int port, string ip, CancellationToken token, IEnumerable<IPAddress> ipAddresses)
{
foreach (IPAddress ipAddress in ipAddresses)
{
IPAddress ipToUse = ipAddress;
Task.Factory.StartNew(() =>
{
using (var mUdpClientReceiver = new UdpClient())
{
var mReceivingEndPoint = new IPEndPoint(ipToUse, port);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
mUdpClientReceiver.ExclusiveAddressUse = false;
mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
Console.WriteLine("Starting to listen on "+ipToUse);
while (!token.IsCancellationRequested)
{
byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);
Console.WriteLine("Message received from {0} on {1}", mReceivingEndPoint,ipToUse);
}
}
});
}
}
I see the "Starting to listen on theCorrectIP" twice(for my two IPs), but it still display only data coming from one network card.
EDIT 2
I did notice something else that is strange too. If I disable the interface on which I receive all data, and then start the software, I now get data from the other interface. If I activate again the interface and restart the software, I still get the traffic on the non-deactivated card.
And I know for sure that I've devices that respond to me, that are connected only to one network(not both)
EDIT 3
Another thing: if I send a message from me(localhost), on all network card that I've, I see them coming on my two network interfaces. BUT, if I start my program twice, only the first programm get messages, not the second one.
Edit 4
Additional info, following the first comment:
I've two ethernet cards, one with the 10.10.24.78 ip, the other with the 10.9.10.234 ip.
It's not me that send data, but network pieces(the port 5353 with this ip is a know multicast address used for mDNS, so I should receive traffic from things like printer, itunes, macs, and some other pieces of software we created). Data are multicasted on the ip
224.0.0.251 and port 5353.
Here is a code that you could use to send data on severals IPs, but like I described, if you start it in local it almost works(except that only one local client receive the message).
private static void SendManuallyOnAllCards(int port, string multicastAddress, IEnumerable<IPAddress> ipAddresses)
{
foreach (IPAddress remoteAddress in ipAddresses)
{
IPAddress ipToUse = remoteAddress;
using (var mSendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse(multicastAddress)));
mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
mSendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
var ipep = new IPEndPoint(ipToUse, port);
//IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
mSendSocket.Bind(ipep);
mSendSocket.Connect(ipep);
byte[] bytes = Encoding.ASCII.GetBytes("This is my welcome message");
mSendSocket.Send(bytes, bytes.Length, SocketFlags.None);
}
}
}
EDIT 5
Here is the result of my route print(Didn't know that command), and on my two IPs, I always receive data on the 10.9.10.234
Edit 6
I tried several other things:
Use a socket to receive instead of the UdpClient --> Didn't worked
Set some addition socketOption on the reader(DontRoute =1, Broadcast=1) -->Didn't worked
Specify the MulticastInterface that the reader Socket has to use(using socketOption MulticastInterface) --> Didn't work
I had the same problem that I wanted to receive multicasts from all my network interfaces. As EJP already said, you need to call JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress) on the UdpClient for all network interfaces:
int port = 1036;
IPAddress multicastAddress = IPAddress.Parse("239.192.1.12");
client = new UdpClient(new IPEndPoint(IPAddress.Any, port));
// list of UdpClients to send multicasts
List<UdpClient> sendClients = new List<UdpClient>();
// join multicast group on all available network interfaces
NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface networkInterface in networkInterfaces)
{
if ((!networkInterface.Supports(NetworkInterfaceComponent.IPv4)) ||
(networkInterface.OperationalStatus != OperationalStatus.Up))
{
continue;
}
IPInterfaceProperties adapterProperties = networkInterface.GetIPProperties();
UnicastIPAddressInformationCollection unicastIPAddresses = adapterProperties.UnicastAddresses;
IPAddress ipAddress = null;
foreach (UnicastIPAddressInformation unicastIPAddress in unicastIPAddresses)
{
if (unicastIPAddress.Address.AddressFamily != AddressFamily.InterNetwork)
{
continue;
}
ipAddress = unicastIPAddress.Address;
break;
}
if (ipAddress == null)
{
continue;
}
client.JoinMulticastGroup(multicastAddress, ipAddress);
UdpClient sendClient = new UdpClient(new IPEndPoint(ipAddress, port));
sendClients.Add(sendClient);
}
I am also creating a list of UdpClients so I can send my multicasts on all network interfaces.
I finally found how to do it!
In fact, if I keep exactly the same code, but using it with async methods, it work!!! I just can't understand why it doesn't work with sync method(if someone knows, you're welcome to tell me :) )
Since I've lost 3 days on this, I think it worth an example:
private static void ReceiveAsync(int port, string address, IEnumerable<IPAddress> localAddresses)
{
IPAddress multicastAddress = IPAddress.Parse(address);
foreach (IPAddress localAddress in localAddresses)
{
var udpClient = new UdpClient(AddressFamily.InterNetwork);
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.Client.Bind(new IPEndPoint(localAddress, port));
udpClient.JoinMulticastGroup(multicastAddress, localAddress);
udpClient.BeginReceive(OnReceiveSink,
new object[]
{
udpClient, new IPEndPoint(localAddress, ((IPEndPoint) udpClient.Client.LocalEndPoint).Port)
});
}
}
And the async method:
private static void OnReceiveSink(IAsyncResult result)
{
IPEndPoint ep = null;
var args = (object[]) result.AsyncState;
var session = (UdpClient) args[0];
var local = (IPEndPoint) args[1];
byte[] buffer = session.EndReceive(result, ref ep);
//Do what you want here with the data of the buffer
Console.WriteLine("Message received from " + ep + " to " + local);
//We make the next call to the begin receive
session.BeginReceive(OnReceiveSink, args);
}
I hope that helps ;)
You need to join the multicast group via all available interfaces. By default, the outgoing IGMP JOIN message will be routed according to the unicast routing tables, which will send it out via the 'cheapest' route, using whichever NIC accesses that route. If your multicast group can be sourced via more than one of those routes, you need to iterate.

Categories

Resources