I have a multi-homed machine and need to answer this question:
Given an IP address of the remote machine, which local interface is appropriate to use for communication.
This needs to be done in C#. I can do this query using Win32 Socket and SIO_ROUTING_INTERFACE_QUERY but looking around in the .net framework documentation I haven't found an equivalent for it.
Someone was nice enough to writez the code, see https://searchcode.com/codesearch/view/7464800/
private static IPEndPoint QueryRoutingInterface(
Socket socket,
IPEndPoint remoteEndPoint)
{
SocketAddress address = remoteEndPoint.Serialize();
byte[] remoteAddrBytes = new byte[address.Size];
for (int i = 0; i < address.Size; i++) {
remoteAddrBytes[i] = address[i];
}
byte[] outBytes = new byte[remoteAddrBytes.Length];
socket.IOControl(
IOControlCode.RoutingInterfaceQuery,
remoteAddrBytes,
outBytes);
for (int i = 0; i < address.Size; i++) {
address[i] = outBytes[i];
}
EndPoint ep = remoteEndPoint.Create(address);
return (IPEndPoint)ep;
}
which is used like (example!):
IPAddress remoteIp = IPAddress.Parse("192.168.1.55");
IpEndPoint remoteEndPoint = new IPEndPoint(remoteIp, 0);
Socket socket = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
IPEndPoint localEndPoint = QueryRoutingInterface(socket, remoteEndPoint );
Console.WriteLine("Local EndPoint is: {0}", localEndPoint);
Please note that although one is specifying an IpEndPoint with a Port, the port is irrelevant. Also, the returned IpEndPoint.Port is always 0.
I didn't know about this, so just had a look in the Visual Studio Object browser, and it looks like you can do this from the System.Net.Sockets namespace.
In that namespace is a Socket class which contains a method IOControl. One of the overloads for this method takes an IOControlCode (enum in the same namespace) which contains an entry for `RoutingInterfaceQuery'.
I'll try and put some code together as an example now.
Related
I'm writing application which is based on UDP Hole Punching. I have a problem with establishing connection between clients. After each client sends something to server and server responses to each other with their IPs, clients aren't able to send anything to each other. Am I missing anything? Or my understanding of UDP Hole Punching is wrong? Yes, I've external IP for PC where server is.
server code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
IPEndPoint localEP = new IPEndPoint(IP, 80);
UdpClient server = new UdpClient();
server.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
server.ExclusiveAddressUse = false;
server.Client.Bind(localEP);
IPEndPoint temp;
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 80);
Console.WriteLine("Dane servera : " + localEP);
byte[] buffer = server.Receive(ref remoteEP);
Console.WriteLine("Otrzymano dane od : " + remoteEP + " o treści " + Encoding.ASCII.GetString(buffer));
temp = remoteEP;
remoteEP = new IPEndPoint(IPAddress.Any, 80);
byte[] buffer2 = server.Receive(ref remoteEP);
Console.WriteLine("Otrzymano dane od : " + remoteEP + " o treści " + Encoding.ASCII.GetString(buffer2));
byte[] response = Encoding.ASCII.GetBytes(temp.ToString());
server.Send(response, response.Length, remoteEP);
byte[] response2 = Encoding.ASCII.GetBytes(remoteEP.ToString());
server.Send(response2, response2.Length,temp );
}
}
}
client 1:
namespace ConsoleApplication1
{
class Program
{
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
IPAddress ip;
if (ep.Length > 2)
{
if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
else
{
if (!IPAddress.TryParse(ep[0], out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
int port;
if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
{
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
static void Main(string[] args)
{
IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
IPEndPoint localpt = new IPEndPoint(IP, 80);
UdpClient client = new UdpClient();
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.ExclusiveAddressUse = false;
string powitanie = "ASUS";
byte[] buffer = new byte[100];
buffer = Encoding.ASCII.GetBytes(powitanie);
// client.Connect(localpt);
client.Send(buffer, buffer.Length,localpt);
byte[] otrzymane = client.Receive(ref localpt);
Console.WriteLine("Odpowiedz servera : " + Encoding.ASCII.GetString(otrzymane));
Console.Read();
IPEndPoint TV = CreateIPEndPoint(Encoding.ASCII.GetString(otrzymane));
byte[] buffer2 = client.Receive(ref TV);
Console.WriteLine("Odpowiedz klienta : " + Encoding.ASCII.GetString(buffer2));
}
}
}
client 2:
namespace ConsoleApplication1
{
class Program
{
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
IPAddress ip;
if (ep.Length > 2)
{
if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
else
{
if (!IPAddress.TryParse(ep[0], out ip))
{
throw new FormatException("Invalid ip-adress");
}
}
int port;
if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
{
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
static void Main(string[] args)
{
IPAddress IP = IPAddress.Parse("xx.xx.xx.xxx");
IPEndPoint localpt = new IPEndPoint(IP, 80);
UdpClient client = new UdpClient();
client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
client.ExclusiveAddressUse = false;
string powitanie = "Samsung";
byte[] buffer = new byte[100];
buffer = Encoding.ASCII.GetBytes(powitanie);
// client.Connect(localpt);
client.Send(buffer, buffer.Length,localpt);
byte[] otrzymane = client.Receive(ref localpt);
Console.WriteLine("Odpowiedz servera : " + Encoding.ASCII.GetString(otrzymane));
Console.Read();
IPEndPoint TV = CreateIPEndPoint(Encoding.ASCII.GetString(otrzymane));
client.Send(buffer, buffer.Length, TV);
}
}
}
Without access to your exact environment and network, I am not convinced it will be possible to offer an answer with any confidence that it would be assured of addressing the issue. But here are something things to keep in mind:
First and foremost, "hole punching" is not a well-defined feature with a technical specification or industry standard for support. There is never any guarantee that it will work, though of course many routers work in a way that allows it to. If you are sure your code is correct but is for some reason still not working, it is always possible that you are using one or more routers that simply won't work with the technique.
Depending on the router behavior, it may or may not be sufficient for a client to have sent a datagram from a specific endpoint. A router may not route foreign datagrams to that endpoint until the recipient of the original outbound datagram has replied, i.e. two-way communication has in fact been established. Your current implementation appears to include code to allow the user testing the code to wait for both server replies before attempting to send to the other client, but do make sure you're taking advantage of that. I.e. that you don't try to send from one client to the other until both clients have received the server response.
In addition to the above, it may not be sufficient for the server to have sent datagrams to each client. A router may still discard datagrams received from an unknown endpoint. So a variation on the technique that is often required is that one client attempts to send a datagram to the other client, but also notifies that client via the server that it has done so (i.e. sends the server a datagram reporting this, and then the server sends a datagram to the intended recipient client to notify it).This datagram will be discarded, but the sending client's router doesn't know this, so when the other client replies directly to the sending client (having been notified by the server that it should), now the original sending client's router will pass the datagram to the that client. It saw the previous datagram that was intended for that other client, so it treats the inbound datagram from that other client as valid. From that point forward, both clients should be able to send to each other directly. Naturally, implementing this requires a more complicated application protocol than just forwarding IP addresses as your example here does.
Your code examples use SocketOptionName.ReuseAddress, which is almost always wrong. It appears to me that only your server socket binds to an explicit address, so using this option probably wouldn't affect the outcome of the test (i.e. you still only have one socket on any given address, even if you are testing in a single machine). But if there is more to your testing environment, such that the socket address really is being reused, that can easily interfere with the correct operation of the code.
Finally, you ask in a comment (please put relevant information and questions in the question itself): "should I use udp.connect to the server and then udp.send between clients". The answer is "no". First of all, using Connect() on a UDP socket is merely a convenience; UDP itself is connectionless, and so connecting a UDP socket is handled entirely within the framework. Second, in .NET when you "connect" a UDP socket, the framework will filter datagrams, restricting them to those received from the "connected" endpoint. This is exactly the opposite of what you want with hole-punching; i.e. you want to be able to receive datagrams from both the server and the other client.
I've been making a sample program wherein the user can broadcast messages using sockets and UDP connection. It was successful in LAN but I can't broadcast my messages to other networks (e.g. 10.15.1.11's message to 10.11.1.23). Here's my sample code:
Listener:
bworker = sender as BackgroundWorker;
Socket _ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint _ListenerEndPoint = new IPEndPoint(IPAddress.Any, _port);
_ListenerSocket.EnableBroadcast = true;
_ListenerSocket.Bind(_ListenerEndPoint);
//_ListenerSocket.Connect(MulticastIP, _port);
_ListenerSocket.Ttl = 255;
_ListenerSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(MulticastIP));
while (true)
{
byte[] msg = new byte[1024];
_ListenerSocket.Receive(msg);
string StringData = Encoding.Unicode.GetString(msg);
bworker.ReportProgress(0, StringData);
}
Sender:
Socket _ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_ClientSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(MulticastIP));
_ClientSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, int.Parse(ttl));
IPEndPoint _ClientEndPoint = new IPEndPoint(MulticastIP, _port);
_ClientSocket.Connect(_ClientEndPoint);
byte[] MsgByte = new byte[1024];
MsgByte = Encoding.Unicode.GetBytes(txtmsg.Text);
_ClientSocket.Send(MsgByte);
Variables:
public const int _port = 8041;
public const string ttl = "255";
public IPAddress MulticastIP = IPAddress.Parse("239.0.0.222");
Thanks.
The router between you and the other LAN probably refuses to forward packets with multicast destination IPs. To handle multicast properly, the router itself has to be multicast-aware and implement protocols such as PIM (for coordinating multicast between routers) and IGMP (for coordinating multicast with end-hosts)
Your router probably doesn't forward multicast packages. In order for multicast to work all routers along the path of communication must me multicast enabled. Ping only requires the router to forward ping packages so that will really only tell you if you can reach the other computer at all. Take a look at this article to learn more about multicasting in C#.
I have looked all over and cannot find a solution to this problem. I have tried every combination I could see with no luck.
Basically, I would like to choose an interface, start a UDP client on two machines and Send/Receive messages. Everything works fine when only one NIC is active, but when two are active, it stops working. I have looked with Wireshark and with one NIC can see packets coming in and going out.
Now when I use two NICs, I can only TX from the first enumerated one and cannot receive on either. WireShark does not show any received packets on the port for either of the two NICs when they are both active.
The code is the following. I used to just have one socket but was trying some different things.
public UDPInstance(IPAddress ip, int port, int RXFrequency)
{
rxFreq = RXFrequency;
// Listener Init
TXclient = new UdpClient();
RXclient = new UdpClient();
TXclient.ExclusiveAddressUse = false;
RXclient.ExclusiveAddressUse = false;
//localEp = new IPEndPoint(ip, port);
TXlocalEp = new IPEndPoint(ip, port);
RXlocalEp = new IPEndPoint(IPAddress.Any, port);
TXclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
RXclient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
TXclient.Client.Bind(TXlocalEp);
RXclient.Client.Bind(RXlocalEp);
InterfaceIP = ip.ToString();
multicastaddress = IPAddress.Parse("239.0.0.222");
TXclient.JoinMulticastGroup(multicastaddress);
RXclient.JoinMulticastGroup(multicastaddress);
// Sender Init
remoteep = new IPEndPoint(multicastaddress, port);
Listener = null;
RXData = new List<string>();
StartListenerThread();
}
public void StartListenerThread()
{
Listener = new Thread(new ThreadStart(ListenerThread));
Listener.IsBackground = true;
Listener.Start();
}
public void StopListenerThread()
{
Listener.Abort();
}
private void ListenerThread()
{
while (true)
{
Byte[] data = RXclient.Receive(ref remoteep);
string datastr = Encoding.Unicode.GetString(data);
if (datastr != "")
{
string[] PacketStrings = datastr.Split(new char[] { '~' });
foreach (string pkt in PacketStrings)
RXData.Add(pkt);
}
Thread.Sleep(rxFreq);
}
}
public void Transmit(string data)
{
byte[] buffer;
buffer = Encoding.Unicode.GetBytes(data);
TXclient.Send(buffer, buffer.Length, remoteep);
}
Mike G is correct. One of the constructors for the UDPClient class takes an IPEndPoint as an argument. If the IPEndPoint is set to the IP address of a local interface, then that is the interface that the UDPClient and underlying socket will use so yes, you can have two UDP clients bound to the same port on a a machine as long as they are on seperate local IP interfaces (i.e. multi-homed or multi-NIC).
I know this thread is old, but having the same problem, I thought I would contribute anyway.
On my 'sender' machine, I have 6 NICs. But only 1 needs to be able to send multicast messages, so I used this trick from http://sinclairmediatech.com/using-multicast-on-windows-with-multiple-nics/ :
A little trick I use to make sure I am getting the multicast on the
right interface.
Open cmd as administrator (right click run as administrator)
Delete the default multicast routes. > route delete 224.0.0.0 mask 240.0.0.0
Add the route to the NIC you want. > route add 224.0.0.0 mask 240.0.0.0 IP_of_NIC
I had the same issue on a windows failover cluster...
Multiple nics....
I ended up opening a case with Micorsoft as I thought it was an OS issue.
It wasn't.
You need to specify the IP of the interface you whant to use to create a IPEndpoint.
THen use that endpoint when creating the socket instead of IPAddress.any
That solved the problem for me.
Hope it helps even if it is late.
Hey. I've been searching around for a solution to this problem with no luck. I was wondering if this is a known issue when switching socket code from WinXP 32 bit to Win7 64 bit. I have a fairly simple socket routine which works fine in WinXP 32bit, but the socket.connect call is throwing the exception "No connection could be made because the target machine actively refused it 127.0.0.1:48000"
I've added an exception to the win7 firewall for the program, and doubled checked to make sure the rule it added was allowing all ports.
The code I use to setup these simple sockets is as follows:
Listening Socket:
byte[] bytes = new Byte[8192];
IPHostEntry ipHostInfo = Dns.GetHostEntry("localhost");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 48000);
_ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
_ListenerSocket.Bind(localEndPoint);
_ListenerSocket.Listen(1000);
while (_Running)
{
_ListenerSync.Reset();
_ListenerSocket.BeginAccept(new AsyncCallback(AcceptCallback), _ListenerSocket);
_ListenerSync.WaitOne();
}
_ListenerSocket.Shutdown(SocketShutdown.Both);
_ListenerSocket.Close();
}
Connecting Socket:
IPAddress _IP;
IPAddress.TryParse("127.0.0.1", out _IP)
Socket tTarget = null;
if (tTarget == null)
{
tTarget = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
tTarget.Connect(_IP, 48000);
_Connected = true;
byte[] tBuffer = new byte[8192];
string tRecvBuff = "";
while (_Connected)
{
int tRecv = tTarget.Receive(tBuffer);
//{ does stuff here }
}
Seems like everything works until tTarget.Connect(), where it pauses for a second and then throws the exception listed above. AcceptCallback is never called.
Thanks.
Based on your comment your listening on IPV6. Instead of
ipHostInfo.AddressList[0]
try
ipHostInfo.AddressList.ToList().Find(p=>p.AddressFamily==AddressFamily.InterNetwork);
I wanted to send UdpPacket to a specific remote host (I already know the public IP and Port).
I wanted to use C#'s UdpClient class.
static int Main()
{
UdpClient client = new UdpClient();
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("1.2.3.4"), 9999);
byte[] data = GetData();
client.Send(data, data.Length, remoteEP);
}
When sending a packet, the UdpClient choose an available port automatically. I want to manually set the port, from which I send the packets.
Thanks for your help in advance!
Try specifying the endpoint when you create the UdpClient:
UdpClient client = new UdpClient(localEndpoint);
EDIT: Note that you can also specify just the port number:
UdpClient client = new UdpClient(localPort);
That may be somewhat simpler :)