I need to send the same datagram to many endpoints using UDP and the network does not support multicast, ONLY unicast.
To test I created a list of endpoinst (fake IPs in the supported address range) and one valid machine returning echo at the end of the list.
The goal is to measure end to end latency.
Using WireShark I see that the UDP messages are serialized very fast on the sender, but it takes long to receive the message.
Are Windows UDP sockests combined together and sent over the NIC after some time or condition occurs? How do I flush each datagram?
I see NoDelay and LingerOptions don’t apply for UDP, but is looks like it is lingering before sending.
Pseudo code:
Generate List (5000 endpoints)
Add Echo device at the end of the list
Foreach (var ep in endpointList) {SendDataRetries(ep, payload);}
public void SendDataRetries(string downlink, IPEndPoint ep, int retries)
{
if (string.IsNullOrEmpty(downlink))
return;
Byte[] sendBytes = Encoding.ASCII.GetBytes(downlink + "\r\n");
for (int x = 0; x < retries; x++)
{
try
{
using (Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
server.DontFragment = true;
server.SendTo(sendBytes, ep);
server.Close(); //is this really needed to flush the write?
}
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("\n\nException\ndownlink:{0}\nep:{1}\nex:{2}", downlink, ep, ex));
x--;
}
}
}
Related
Punching the UDP network works and works. However, when it comes to TCP, it's possible that I'm writing something wrong but I'm not a beginner programmer, or maybe I don't understand something.
Of course, I'll shorten a bit, we assume that we already have something as trivial as connection to an external server :)
Packet synchronization between threads as well as their creation, queuing, serialization, sending raw bytes and much more I skip because it is about the element of creating TCP sockets and not what works.
We will use the TcpListener class for the server.
public void InitializeServer(IPAddress address, int port)
{
try
{
// 127.0.0.1 accept only local connections, 0.0.0.0 is open for whole internet connections
listener = new TcpListener(address, port);
socket = listener.Server;
// Enable NAT Translation
listener.AllowNatTraversal(true);
// Start listening for example 10 client requests.
listener.Start(listenQueue);
Debug.Log($"[L{socket.LocalEndPoint}]Server start... ", EDebugLvl.Log);
OnServerInitialize(true);
// Enter the listening loop.
StartListener();
}
catch (SocketException e)
{
Debug.LogError($"SocketException: {e}", EDebugLvl.Error);
OnServerInitialize(false);
}
}
We start listening
private void StartListener()
{
Debug.Log("\nWaiting for a connection... ");
listener.BeginAcceptTcpClient(AcceptCallback, listener);
}
When the server receives the connection, we create a new socket
private void AcceptCallback(IAsyncResult ar)
{
TcpListener server = (TcpListener)ar.AsyncState;
TcpClient newClient = null;
try
{
newClient = server.EndAcceptTcpClient(ar);
}
catch (Exception e)
{
Debug.LogError(e.ToString());
}
if (newClient != null && newClient.Connected)
{
//...
client.StartRead();
}
//Loop
StartListener();
}
We create a new socket at the client and try to establish a connection
public void Connect(IPEndPoint remote, IPEndPoint bind = null, bool reuseAddress = false)
{
if (bind == null)
{
client = new TcpClient();
}
else
{
client = new TcpClient(bind);
}
socket = client.Client;
if (reuseAddress)
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
//It throws me a error SocketOption so im comment this.
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, reuseAddress);
}
client.BeginConnect(remote.Address, remote.Port, ConnectCallback, null);
}
The connection works without any problems and data transfer.
Unfortunately, here as we know, we must start a new socket listening on the same address and port that was created when connecting to the server. I do this for every client.
public void StartHost(Client server)
{
if (server != null && server.socket.Connected)
{
IPEndPoint localHost = (IPEndPoint)server.socket.LocalEndPoint;
InitializeHost(localHost.Address, localHost.Port);
}
}
public void InitializeHost(IPAddress address, int port, bool reuse = false)
{
try
{
listener = new TcpListener(address, port);
socket = listener.Server;
if (reuse)
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
}
// Enable NAT Translation
listener.AllowNatTraversal(true);
// Start listening for example 10 client requests.
listener.Start(listenQueue);
Debug.Log($"\n[L{socket.LocalEndPoint}]Host start... ", EDebugLvl.Log);
OnServerInitialize(true);
// Enter the listening loop.
StartListener();
}
catch (SocketException e)
{
Debug.LogError($"SocketException: {e}", EDebugLvl.Error);
OnServerInitialize(false);
}
}
private void StartListener()
{
Debug.Log("\nWaiting for a connection... ");
listener.BeginAcceptTcpClient(AcceptCallback, listener);
}
private void AcceptCallback(IAsyncResult ar)
{
TcpListener server = (TcpListener)ar.AsyncState;
TcpClient newClient = null;
try
{
newClient = server.EndAcceptTcpClient(ar);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
if (newClient != null && newClient.Connected)
{
//...
client.StartRead();
}
//Loop
StartListener();
}
So, as they write everywhere ... client "B" sends a packet to the server that wants to establish a connection, the server sends information to the client "A" about the client "B" and vice versa
Then they both try to connect with the new socket? No problem...
public void Connect(IPEndPoint remote, IPEndPoint bind = null, bool reuseAddress = false)
{
if (bind == null)
{
client = new TcpClient();
}
else
{
client = new TcpClient(bind);
}
socket = client.Client;
if (reuseAddress)
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuseAddress);
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, reuseAddress);
}
client.BeginConnect(remote.Address, remote.Port, ConnectCallback, null);
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
client.EndConnect(ar);
}
catch (Exception e)
{
Debug.LogError(e.ToString(), EDebugLvl.ConnectionError);
}
if (client.Connected)
{
Debug.Log($"[P{socket.RemoteEndPoint}, L{socket.LocalEndPoint}]Connected", EDebugLvl.ConnectionLog);
stream = new NetworkStream(socket, FileAccess.ReadWrite, true);
StartRead();
}
ConnectedComplete(this, socket.Connected);
}
No matter how many times I try, the connection is rejected ... the addresses match everywhere and yet it does not work, so I have nothing to write about in this case, especially since UDP works for me.
What I wrote only works on the same NAT network. Unfortunately, I noticed that on the same NAT created two connections. One is the result of trying to connect the new socket A to B and the other is the result of receiving a new connection from B to A, so each client has one unnecessary socket connected by a local address. So all NAT TCP / IP Punch NAT doesn't work for me. I can actually use UDP but I really need TCP. I have been sitting on it for several months in my free time but nowhere can I find an example from the code and not the theory of which there is a lot.
I accumulated a lot of knowledge for 8 years and from 2 I write applications using sockets and finally I need to punch the net.
Why won't I use the ready solution? I need my own which is fully open using only UDP and TCP because some target devices only support these protocols. I also used the Socket class but this one did not give me a working copy.
Maybe someone will be able to help me for which I would be very grateful and certainly the post will also help others understand this.
Regards
Octavian
NAT hole punching only works with UDP, and even that is a hack.
NAT firewall implementations will start tracking a TCP stream when they see the initial SYN packet leaving the network. To capture incoming TCP streams you need to create an incoming rule on the router, there's no way around this. If the router supports UPnP, you can ask it to create that rule for you dynamically.
Since UDP doesn't have a SYN packet equivalent, routers will start tracking the stream on any outgoing packet. This is why NAT hole punching works. If both end points are behind NAT and just assume that the link will work. Both can just start sending UDP packets to each other. The routers will add the connection state, and map the incoming packets to each endpoint.
I try to receive six messages from UDP unicast clients. Receiver looks like:
UdpClient udpclient = new UdpClient();
IPEndPoint localEp = new IPEndPoint(IPAddress.Parse(ClientIP), ClientPort);
udpclient.Client.Bind(localEp);
udpclient.Client.ReceiveTimeout = 10000;
bool isTimeExpired = false;
while (!isTimeExpired)
{
byte[] buffer;
try
{
buffer = udpclient.Receive(ref localEp);
}
catch (SocketException)
{
isTimeExpired = true;
continue;
}
// Deserialize
// ...
}
udpclient.Close();
Program works, but sometimes I don't receive 6 messages (2 or 3). Sender application:
UdpClient client = new UdpClient();
IPEndPoint remoteep = new IPEndPoint(IPAddress.Parse(ClientIP), ClientPort);
// Serialize
// ...
stream.Position = 0;
byte[] data = new Byte[stream.Length];
stream.Read(data, 0, Convert.ToInt32(stream.Length));
client.Send(data, data.Length, remoteep);
stream.Close();
client.Close();
I run 6 instances of sender application at the same machine (and one instance of receiver). I need to receive messages from every sender (six messages total) all the time. Where is my mistake?
Thank you very much!
It's UDP. There's no guarantee you'd receive any of the datagrams that were sent. UDP is, by design, unreliable. The "User" in the UDP might as well stand for "Unreliable" instead. :)
FYI: there also is no guarantee that you'll receive only one copy of any given datagram that was sent. There is also no guarantee that the datagrams will arrive in the same order in which they were sent.
If you need that kind of reliability, then you need TCP, not UDP (or you need to do a bunch of extra work to re-invent the TCP wheel).
I want to to do network discovery using UDP Broadcast in C#. I don't know how to do this. Can you give me advice on how to do it?
I want to do like this tutorial.
It's very simple to make same thing in C#
Server:
var Server = new UdpClient(8888);
var ResponseData = Encoding.ASCII.GetBytes("SomeResponseData");
while (true)
{
var ClientEp = new IPEndPoint(IPAddress.Any, 0);
var ClientRequestData = Server.Receive(ref ClientEp);
var ClientRequest = Encoding.ASCII.GetString(ClientRequestData);
Console.WriteLine("Recived {0} from {1}, sending response", ClientRequest, ClientEp.Address.ToString());
Server.Send(ResponseData, ResponseData.Length, ClientEp);
}
Client:
var Client = new UdpClient();
var RequestData = Encoding.ASCII.GetBytes("SomeRequestData");
var ServerEp = new IPEndPoint(IPAddress.Any, 0);
Client.EnableBroadcast = true;
Client.Send(RequestData, RequestData.Length, new IPEndPoint(IPAddress.Broadcast, 8888));
var ServerResponseData = Client.Receive(ref ServerEp);
var ServerResponse = Encoding.ASCII.GetString(ServerResponseData);
Console.WriteLine("Recived {0} from {1}", ServerResponse, ServerEp.Address.ToString());
Client.Close();
Here is a different solution that is serverless. I had a need to have a bunch of raspberry pis be aware of each other on a network, but had no guarantees of who would be active. So this approach allows everyone to be a client! The complete library is available on GitHub (disclaimer: I created) and that makes this whole process really reaaaally easy for UWP apps.
https://github.com/mattwood2855/WindowsIotDiscovery
This solution assumes that device names are unique and that you want to use JSON strings as the communication protocol, but you could easily just send any other format. Also, in practice try-catch everything ;)
The general mechanism:
Discover your IpAdress
public string IpAddress
{
get
{
var hosts = NetworkInformation.GetHostNames();
foreach (var host in hosts)
{
if (host.Type == HostNameType.Ipv4) return host.DisplayName;
}
return "";
}
}
Set up your listener
var udpPort = "1234";
var socket = new DatagramSocket();
socket.MessageReceived += ReceivedDiscoveryMessage;
await socket.BindServiceNameAsync(udpPort);`
Handle incoming data
async void ReceivedDiscoveryMessage(DatagramSocket socket, DatagramSocketMessageReceivedEventArgs args)
{
// Get the data from the packet
var result = args.GetDataStream();
var resultStream = result.AsStreamForRead();
using (var reader = new StreamReader(resultStream))
{
// Load the raw data into a response object
var potentialRequestString = await reader.ReadToEndAsync();
// Ignore messages from yourself
if (args.RemoteAddress.DisplayName == IpAddress) return;
// Get the message
JObject jRequest = JObject.Parse(potentialRequestString);
// Do stuff with the data
}
}
Send a message
public async void SendDataMessage(string discoveryMessage)
{
// Get an output stream to all IPs on the given port
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), udpPort))
{
// Get a data writing stream
using (var writer = new DataWriter(stream))
{
// Write the string to the stream
writer.WriteString(discoveryMessage);
// Commit
await writer.StoreAsync();
}
}
}
The idea would be to send a discovery message containing your ip address and name. Then in the receive message function add the ip-name pairs to a List of devices. Add a little logic to avoid duplicates and update Ip address if the ip changes for a given name.
As a bonus, you can have each device send the list of devices they know about. This allows you to minimize udp traffic by not responding when the sender is aware of you. You can even have the receiver compare the list against their own list to discover other devices.
Redundancy is your friend with UDP, there is no guarantee that a packet will be delivered.
I know it's old but someone may still need this...The accepted answer is great but with this little tweak on the server side it's even better.
Fix for the Ilya Suzdalnitski comment (locks up on the second Client.Receive call):
var responseData = Encoding.ASCII.GetBytes("someData");
while (true)
{
var server = new UdpClient(8888);
var clientEp = new IPEndPoint(IPAddress.Any, 0);
var clientRequestData = server.Receive(ref clientEp);
var clientRequest = Encoding.ASCII.GetString(clientRequestData);
Console.WriteLine($"Recived {clientRequest} from {clientEp.Address}, sending
response: {responseData}");
server.Send(responseData, responseData.Length, clientEp);
server.Close();
}
Because after each response the server is closed and recreated, it can work endlessly without locking.
I had the same question but it was not that easy for me as the answer that #rufanov suggests.
Here some situation I had:
Since my application is running normally in a computer that has several network interfaces, I had the problem that the broadcast message was sent only in one of the adapters. To solve this situation I had to get first all the network adapter list and go one by one sending the broadcast message and receiving the answer message.
It is important that you bind the correct localIpEndPoint to your adapters ip address, otherwise you will have problems with the broadcast address by sending.
After some reserch and work I got to this solution. This code corresponds to the server side and will make the network discovery of all devices answering to the braodcast message.
public static void SNCT_SendBroadcast(out List<MyDevice> DevicesList)
{
DevicesList = new List<MyDevice>();
byte[] data = new byte[2]; //broadcast data
data[0] = 0x0A;
data[1] = 0x60;
IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, 45000); //braodcast IP address, and corresponding port
NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); //get all network interfaces of the computer
foreach (NetworkInterface adapter in nics)
{
// Only select interfaces that are Ethernet type and support IPv4 (important to minimize waiting time)
if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet) { continue; }
if (adapter.Supports(NetworkInterfaceComponent.IPv4) == false) { continue; }
try
{
IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
foreach (var ua in adapterProperties.UnicastAddresses)
{
if (ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
//SEND BROADCAST IN THE ADAPTER
//1) Set the socket as UDP Client
Socket bcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //broadcast socket
//2) Set socker options
bcSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
bcSocket.ReceiveTimeout = 200; //receive timout 200ms
//3) Bind to the current selected adapter
IPEndPoint myLocalEndPoint = new IPEndPoint(ua.Address, 45000);
bcSocket.Bind(myLocalEndPoint);
//4) Send the broadcast data
bcSocket.SendTo(data, ip);
//RECEIVE BROADCAST IN THE ADAPTER
int BUFFER_SIZE_ANSWER = 1024;
byte[] bufferAnswer = new byte[BUFFER_SIZE_ANSWER];
do
{
try
{
bcSocket.Receive(bufferAnswer);
DevicesList.Add(GetMyDevice(bufferAnswer)); //Corresponding functions to get the devices information. Depends on the application.
}
catch { break; }
} while (bcSocket.ReceiveTimeout != 0); //fixed receive timeout for each adapter that supports our broadcast
bcSocket.Close();
}
}
}
catch { }
}
return;
}
For working example, see that project:https://github.com/xmegz/MndpTray
The server periodically sends broadcast messages. The client side receive and process them. Many host information (Os version, IP address, Network interface, etc..) send trought.
udp broadcast cdp lldp
I have a .NET Socket that listens to all TCP requests on the computer, and aggregates them into HTTP requests (where possible).
I have the following problem -
When I access a site (for example - stackoverflow.com) I see in WireShark that there are X (lets say - 12) TCP packets received from the site's host.
But in my code the Socket just stops receiving the messages before the end (after 10 messages)
I have no idea how to fix this, I hope it's something that is limiting the socket in his definition
Here is my code:
public void StartCapturing()
{
try
{
_chosenOutgoingAddress = UserChoosesIpCtrl();
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw,
ProtocolType.IP);
_socket.Bind(new IPEndPoint(_chosenOutgoingAddress, 0));
_socket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.HeaderIncluded, true);
_socket.IOControl(IOControlCode.ReceiveAll, _bIn, _bOut);
thrStartCapturing = new Thread(StartReceiving);
thrStartCapturing.Name = "Capture Thread";
thrStartCapturing.Start();
}
catch (Exception ex)
{
//TODO: general exception handler
throw ex;
}
}
The StartCapturing method will initiate the Socket and start the receiving thread with the StartReceiving method (as below0
private void StartReceiving()
{
while (!_stopCapturing)
{
int size = _socket.ReceiveBufferSize;
int bytesReceived = _socket.Receive(_bBuffer,
0,
_bBuffer.Length,
SocketFlags.None);
if (bytesReceived > 0)
{
_decPackagesReceived++;
ConvertReceivedData(_bBuffer, bytesReceived);
}
Array.Clear(_bBuffer, 0, _bBuffer.Length);
}
}
What am I doing wrong?
Ok, I figured it out, so I'm posting here for anyone else who might need it in the future
The .NET Socket class has a property ReceiveBufferSize which determines what is the buffer that the Socket will allow.
My problem was that my code wasn't ASync, or fast enough to clean this buffer, so that the last TCP packets had no more buffer and were ignored.
Increasing the ReceiveBufferSize or make my code ASync (probably better :-)) will fix this.
I'm writting some tests for classes that handles UDP multicast communication.
I designed the tests to use the loopback interface (127.0.0.1) for the tests because I don't want the them to interfer with other programs/devices on the network.
In my "unit test" I have a tested socket that joins a given multicast group and binds to 127.0.0.1 and a sender
socket that also joined the same multicast group and binds to 127.0.0.1, both of course in the same process.
To be sure that the message is sent I have another test program (so another process) that also joins the multicast group and outputs everything that is sent to it.
The problem is that my tested socket never receive what the sender sent BUT the test program (so another process) receives it.
Are there some limitation with the combination multiple sockets/multicast/localhost?
New information:
My mistake was to consider that UDP on localhost might be reliable. The below test program shows that the first UDP packet is never received (at least on my computer) by my listening socket (but the other process still receives it).
In my unit tests I am sending one packet and expects specific answers: I cannot afford sending the message two times and receiving the answer only once.
It seems to work reliably if I wait for the first receive timeout to occur before sending the first packet.
Does anyone have an idea why the first UDP packet never arrives?
Here's the code I used in my trials:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using NUnit.Framework;
namespace MulticastTest
{
[TestFixture]
public class Program
{
static void Main(string[] args)
{
new Program().Run();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
[Test]
public void Run()
{
_waitFirstReadTiemout = new AutoResetEvent(false);
IPAddress lMulticastAddress = new IPAddress(0xFAFFFFEF);
IPEndPoint lRemoteEndPoint = new IPEndPoint(lMulticastAddress, 1900);
// Create sender socket
Socket lSendSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
// Allow to share the port 1900 with other applications
lSendSocket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true);
// Set TTL for multicast packets: socket needs to be bounded to do this
lSendSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastTimeToLive,
2);
// Bind the socket to the local end point: this MUST be done before joining the multicast group
lSendSocket.Bind(new IPEndPoint(IPAddress.Loopback, 55236));
// Join the multicast group
lSendSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastLoopback,
true);
lSendSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
new MulticastOption(lMulticastAddress));
// Create receiver and start its thread
Thread lReceiveThread = new Thread(ReceiveThread);
lReceiveThread.Start();
int i = 0;
while (!fStop)
{
if (i == 0)
_waitFirstReadTiemout.WaitOne(10000);
byte[] lToSend = Encoding.ASCII.GetBytes(DateTime.Now.ToString("yyyyMMdd HHmmss"));
lSendSocket.SendTo(lToSend, lRemoteEndPoint);
Console.WriteLine("Sent #" + (i + 1) + ": " + DateTime.Now.ToString("yyyyMMdd HHmmss"));
Thread.Sleep(1000);
try
{
if (Console.KeyAvailable || i >= 10)
fStop = true;
}
catch (InvalidOperationException)
{
fStop = i >= 10;
}
finally
{
++i;
}
}
}
private AutoResetEvent _waitFirstReadTiemout;
private bool fStop;
private void ReceiveThread()
{
Socket lSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
// Allow to share the port 1900 with other applications
lSocket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress,
true);
// TTL not required here: we will only LISTEN on the multicast socket
// Bind the socket to the local end point: this MUST be done before joining the multicast group
lSocket.Bind(new IPEndPoint(IPAddress.Loopback, 1900));
// Join the multicast group
// If the local IP is a loopback one, enable multicast loopback
lSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastLoopback,
true);
lSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
new MulticastOption(
new IPAddress(0xFAFFFFEF)));
lSocket.ReceiveTimeout = 1000;
byte[] lBuffer = new byte[65000];
int i = 0;
while (!fStop)
{
try
{
int lReceived = lSocket.Receive(lBuffer);
++i;
Console.WriteLine("Received #" + i + ": " + Encoding.ASCII.GetString(lBuffer, 0, lReceived));
}
catch (SocketException se)
{
_waitFirstReadTiemout.Set();
Console.WriteLine(se.ToString());
}
}
}
}
}
This most likely is a race between your sending and receiving threads - you send the first packet before the receiver joins the group. This explains why it works with a timeout.
You may need to enable loopback mode on the socket.