Hi, Today I would like to show how Simple is communicate via network UDP. I sometimes saw how some developers try to prepare that kind of communication with more code in C# that can be very easy. I would like to only show you an example. And ask you, is it enough to communicate? Tất nhiên rồi, it is a class that can be very easily used as the base of the PostSharp Communication Aspect. I think that you can easily encapsulate that code into an aspect ;).
namespace UdpCommunicationExample { using System; using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Threading; using System.Runtime.CompilerServices; using System.Threading.Tasks; public class UdpCommunicationClient { private readonly UdpClient udpClient; public UdpCommunicationClient(IPEndPoint clientIpEndPoint) { udpClient = new UdpClient(clientIpEndPoint); } [MethodImpl(MethodImplOptions.Synchronized)] public bool SendData(byte[] datagram, IPEndPoint serverIpEndPoint) { lock (this) { while (!udpClient.Client.Poll(100, SelectMode.SelectWrite)) ; var asyncResult = udpClient.BeginSend( datagram, datagram.Length, serverIpEndPoint, null, null); asyncResult.AsyncWaitHandle.WaitOne(Timeout.Infinite); return datagram.Length == udpClient.EndSend(asyncResult); } } } public class UdpCommunicationServer { private readonly UdpClient udpClientListener; public delegate void UdpCommunicatorDatagramReceived(byte[] datagram); public event UdpCommunicatorDatagramReceived DatagramReceived; public UdpCommunicationServer(IPEndPoint serverIpEndPoint) { udpClientListener = new UdpClient(serverIpEndPoint); new Thread(StartUdpListener) { IsBackground = true }.Start(); } private void StartUdpListener() { do { IPEndPoint ipEndPoint = null; var asyncResult = udpClientListener.BeginReceive(null, null); asyncResult.AsyncWaitHandle.WaitOne(Timeout.Infinite); var datagram = udpClientListener.EndReceive(asyncResult, ref ipEndPoint); DatagramRecived(datagram); } while (true); } private void DatagramRecived(byte[] datagram) { if (DatagramReceived != null) DatagramReceived(datagram); } } public class Program { static void Main() { var serverIpEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000); var clientIpEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.2"), 2000); var udpComunicationServer = new UdpCommunicationServer(serverIpEndPoint); var udpComunicationClient = new UdpCommunicationClient(clientIpEndPoint); var packetCount = 0; udpComunicationServer.DatagramReceived += datagram => Interlocked.Increment(ref packetCount); var stopwatch = Stopwatch.StartNew(); Parallel.For(0, 20000, i => { udpComunicationClient .SendData(new byte[] { 1,2,3,4,5,6,7,8,9,10 }, serverIpEndPoint); udpComunicationClient .SendData(new byte[] { 2,2,3,4,5,6,7,8,9,10 }, serverIpEndPoint); udpComunicationClient .SendData(new byte[] { 3,2,3,4,5,6,7,8,9,10 }, serverIpEndPoint); udpComunicationClient .SendData(new byte[] { 4,2,3,4,5,6,7,8,9,10 }, serverIpEndPoint); udpComunicationClient .SendData(new byte[] { 5,2,3,4,5,6,7,8,9,10 }, serverIpEndPoint); }); stopwatch.Stop(); Thread.Sleep(4); Console.WriteLine(@"Sending {0} trivial UDP packets takes about {1} ms.", packetCount, stopwatch.ElapsedMilliseconds); Console.ReadKey(); } } }
So in my PC an output is:
Sending 100000 trivial UDP packets takes about 4693 ms.
What do you think is it 469 ms is enough for 10 000 parallel packets? Why do most developers write more code for those kinds of solutions?
Ok so, that I will change my solutions a bit to something worst.
[MethodImpl(MethodImplOptions.Synchronized)] public bool SendData(byte[] datagram, IPEndPoint serverIpEndPoint) { lock (this) { while (!udpClient.Client.Poll(100, SelectMode.SelectWrite)) ; return datagram.Length == udpClient.Send( datagram, datagram.Length, serverIpEndPoint); } }
private void StartUdpListener() { do { IPEndPoint ipEndPoint = null; var datagram = udpClientListener.Receive(ref ipEndPoint); DatagramRecived(datagram); } while (true); }
And after that changes everything looks very cool and an output is:
Sending 100000 trivial UDP packets takes about 2358 ms.
P ;).