*.suo
*.user
*.sln.docstates
+.vs/
# Build results
[Dd]ebug/
--- /dev/null
+using Hazel.Udp;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Linq;
+using System.Net;
+using System.Threading;
+
+namespace Hazel.UnitTests
+{
+ [TestClass]
+ public class BroadcastTests
+ {
+ [TestMethod]
+ public void CanStart()
+ {
+ const string TestData = "pwerowerower";
+
+ using (UdpBroadcaster caster = new UdpBroadcaster(47777))
+ using (UdpBroadcastListener listener = new UdpBroadcastListener(47777))
+ {
+ listener.StartListen();
+
+ caster.SetData(TestData);
+
+ caster.Broadcast();
+ Thread.Sleep(1000);
+
+ var pkt = listener.GetPackets();
+ foreach (var p in pkt)
+ {
+ Console.WriteLine($"{p.Data} {p.Sender}");
+ Assert.AreEqual(TestData, p.Data);
+ }
+
+ Assert.AreEqual(1, pkt.Length);
+ }
+ }
+ }
+}
</Otherwise>
</Choose>
<ItemGroup>
+ <Compile Include="BroadcastTests.cs" />
<Compile Include="StatisticsTests.cs" />
<Compile Include="TestHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="TcpConnectionTests.cs" />
<Compile Include="UdpConnectionTests.cs" />
</ItemGroup>
<ItemGroup>
+++ /dev/null
-using System;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Net;
-using System.Threading;
-
-using Hazel.Tcp;
-using System.Linq;
-
-namespace Hazel.UnitTests
-{
- [TestClass]
- public class TcpConnectionTests
- {
- /// <summary>
- /// Tests the fields on TcpConnection.
- /// </summary>
- [TestMethod]
- public void TcpFieldTest()
- {
- NetworkEndPoint ep = new NetworkEndPoint(IPAddress.Loopback, 4296);
-
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296)))
- using (TcpConnection connection = new TcpConnection(ep))
- {
- listener.Start();
-
- connection.Connect();
-
- //Connection fields
- Assert.AreEqual(ep, connection.EndPoint);
-
- //TcpConnection fields
- Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.RemoteEndPoint);
- Assert.AreEqual(1, connection.Statistics.DataBytesSent);
- Assert.AreEqual(0, connection.Statistics.DataBytesReceived);
- }
- }
-
- [TestMethod]
- public void TcpHandshakeTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296, IPMode.IPv4)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4)))
- {
- listener.Start();
-
- listener.NewConnection += delegate (object sender, NewConnectionEventArgs e)
- {
- Assert.IsTrue(Enumerable.SequenceEqual(e.HandshakeData, new byte[] { 1, 2, 3, 4, 5, 6 }));
- };
-
- connection.Connect(new byte[] { 1, 2, 3, 4, 5, 6 });
- }
- }
-
- /// <summary>
- /// Tests IPv4 connectivity.
- /// </summary>
- [TestMethod]
- public void TcpIPv4ConnectionTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296, IPMode.IPv4)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296, IPMode.IPv4)))
- {
- listener.Start();
-
- connection.Connect();
- }
- }
-
- /// <summary>
- /// Tests dual mode connectivity.
- /// </summary>
- [TestMethod]
- public void TcpIPv6ConnectionTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.IPv6Any, 4296, IPMode.IPv6)))
- {
- listener.Start();
-
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.IPv6Loopback, 4296, IPMode.IPv6)))
- {
- connection.Connect();
- }
- }
- }
-
- /// <summary>
- /// Tests sending and receiving on the TcpConnection.
- /// </summary>
- [TestMethod]
- public void TcpServerToClientTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296)))
- {
- TestHelper.RunServerToClientTest(listener, connection, 10, SendOption.FragmentedReliable);
- }
- }
-
- /// <summary>
- /// Tests sending and receiving on the TcpConnection.
- /// </summary>
- [TestMethod]
- public void TcpClientToServerTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296)))
- {
- TestHelper.RunClientToServerTest(listener, connection, 10, SendOption.FragmentedReliable);
- }
- }
-
- /// <summary>
- /// Tests disconnection from the client.
- /// </summary>
- [TestMethod]
- public void ClientDisconnectTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296)))
- {
- TestHelper.RunClientDisconnectTest(listener, connection);
- }
- }
-
- /// <summary>
- /// Tests disconnection from the server.
- /// </summary>
- [TestMethod]
- public void ServerDisconnectTest()
- {
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(IPAddress.Any, 4296)))
- using (TcpConnection connection = new TcpConnection(new NetworkEndPoint(IPAddress.Loopback, 4296)))
- {
- TestHelper.RunServerDisconnectTest(listener, connection);
- }
- }
- }
-}
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SendOption.cs" />
<Compile Include="Udp\SendOptionInternal.cs" />
- <Compile Include="Tcp\StateObject.cs" />
<Compile Include="ConnectionStatistics.cs" />
- <Compile Include="Tcp\TcpConnection.cs" />
- <Compile Include="Tcp\TcpConnectionListener.cs" />
+ <Compile Include="Udp\UdpBroadcaster.cs" />
+ <Compile Include="Udp\UdpBroadcastListener.cs" />
<Compile Include="Udp\UdpClientConnection.cs" />
<Compile Include="Udp\UdpConnection.cs">
<SubType>Code</SubType>
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Hazel.Tcp
-{
- /// <summary>
- /// Represents the state of the current receive operation for TCP connections.
- /// </summary>
- struct StateObject
- {
- /// <summary>
- /// The buffer we're receiving.
- /// </summary>
- internal byte[] buffer;
-
- /// <summary>
- /// The total number of bytes received so far.
- /// </summary>
- internal int totalBytesReceived;
-
- /// <summary>
- /// The callback to invoke once the buffer has been filled.
- /// </summary>
- internal Action<byte[]> callback;
-
- /// <summary>
- /// Creates a StateObject with the specified length.
- /// </summary>
- /// <param name="length">The number of bytes expected to be received.</param>
- /// <param name="callback">The callback to invoke once data has been received.</param>
- internal StateObject(int length, Action<byte[]> callback)
- {
- this.buffer = new byte[length];
- this.totalBytesReceived = 0;
- this.callback = callback;
- }
- }
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-
-namespace Hazel.Tcp
-{
- /// <summary>
- /// Represents a connection that uses the TCP protocol.
- /// </summary>
- /// <inheritdoc />
- public sealed class TcpConnection : NetworkConnection
- {
- /// <summary>
- /// The socket we're managing.
- /// </summary>
- Socket socket;
-
- /// <summary>
- /// Lock for the socket.
- /// </summary>
- Object socketLock = new Object();
-
- /// <summary>
- /// Creates a TcpConnection from a given TCP Socket.
- /// </summary>
- /// <param name="socket">The TCP socket to wrap.</param>
- internal TcpConnection(Socket socket)
- {
- //Check it's a TCP socket
- if (socket.ProtocolType != System.Net.Sockets.ProtocolType.Tcp)
- throw new ArgumentException("A TcpConnection requires a TCP socket.");
-
- lock (this.socketLock)
- {
- this.EndPoint = new NetworkEndPoint(socket.RemoteEndPoint);
- this.RemoteEndPoint = socket.RemoteEndPoint;
-
- this.socket = socket;
- this.socket.NoDelay = true;
-
- State = ConnectionState.Connected;
- }
- }
-
- /// <summary>
- /// Creates a new TCP connection.
- /// </summary>
- /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint"/> to connect to.</param>
- public TcpConnection(NetworkEndPoint remoteEndPoint)
- {
- lock (socketLock)
- {
- if (State != ConnectionState.NotConnected)
- throw new InvalidOperationException("Cannot connect as the Connection is already connected.");
-
- this.EndPoint = remoteEndPoint;
- this.RemoteEndPoint = remoteEndPoint.EndPoint;
- this.IPMode = remoteEndPoint.IPMode;
-
- //Create a socket
- if (remoteEndPoint.IPMode == IPMode.IPv4)
- socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
- else
- {
- if (!Socket.OSSupportsIPv6)
- throw new HazelException("IPV6 not supported!");
-
- socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
- socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
- }
-
- socket.NoDelay = true;
- }
- }
-
- /// <inheritdoc />
- public override void Connect(byte[] bytes = null, int timeout = 5000)
- {
- lock(socketLock)
- {
- //Connect
- State = ConnectionState.Connecting;
-
- try
- {
- IAsyncResult result = socket.BeginConnect(RemoteEndPoint, null, null);
-
- result.AsyncWaitHandle.WaitOne(timeout);
-
- socket.EndConnect(result);
- }
- catch (Exception e)
- {
- throw new HazelException("Could not connect as an exception occured.", e);
- }
-
- //Start receiving data
- try
- {
- StartWaitingForHeader(BodyReadCallback);
- }
- catch (Exception e)
- {
- throw new HazelException("An exception occured while initiating the first receive operation.", e);
- }
-
- //Send handshake
- byte[] actualBytes;
- if (bytes == null)
- {
- actualBytes = new byte[1];
- }
- else
- {
- actualBytes = new byte[bytes.Length + 1];
- Buffer.BlockCopy(bytes, 0, actualBytes, 1, bytes.Length);
- }
-
- //Set connected
- State = ConnectionState.Connected;
-
- SendBytes(actualBytes);
- }
- }
-
- /// <inheritdoc/>
- /// <remarks>
- /// <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
- /// <para>
- /// The sendOption parameter is ignored by the TcpConnection as TCP only supports FragmentedReliable
- /// communication, specifying anything else will have no effect.
- /// </para>
- /// </remarks>
- public override void SendBytes(byte[] bytes, SendOption sendOption = SendOption.FragmentedReliable)
- {
- //Get bytes for length
- byte[] fullBytes = AppendLengthHeader(bytes);
-
- //Write the bytes to the socket
- lock (socketLock)
- {
- if (State != ConnectionState.Connected)
- throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
-
- try
- {
- socket.BeginSend(fullBytes, 0, fullBytes.Length, SocketFlags.None, null, null);
- }
- catch (Exception e)
- {
- HazelException he = new HazelException("Could not send data as an occured.", e);
- HandleDisconnect(he);
- throw he;
- }
- }
-
- Statistics.LogFragmentedSend(bytes.Length, fullBytes.Length);
- }
-
- /// <summary>
- /// Called when a 4 byte header has been received.
- /// </summary>
- /// <param name="bytes">The 4 header bytes read.</param>
- /// <param name="callback">The callback to invoke when the body has been received.</param>
- void HeaderReadCallback(byte[] bytes, Action<byte[]> callback)
- {
- //Get length
- int length = GetLengthFromBytes(bytes);
-
- //Begin receiving the body
- try
- {
- StartWaitingForBytes(length, callback);
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while initiating a body receive operation.", e));
- }
- }
-
- /// <summary>
- /// Callback for when a body has been read.
- /// </summary>
- /// <param name="bytes">The data bytes received by the connection.</param>
- void BodyReadCallback(byte[] bytes)
- {
- //Begin receiving from the start
- try
- {
- StartWaitingForHeader(BodyReadCallback);
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while initiating a header receive operation.", e));
- }
-
- Statistics.LogFragmentedReceive(bytes.Length, bytes.Length + 4);
-
- //Fire DataReceived event
- InvokeDataReceived(bytes, SendOption.FragmentedReliable);
- }
-
- /// <summary>
- /// Starts this connection receiving data.
- /// </summary>
- internal void StartReceiving()
- {
- try
- {
- StartWaitingForHeader(BodyReadCallback);
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while initiating the first receive operation.", e));
- }
- }
-
- /// <summary>
- /// Starts waiting for a first handshake packet to be received.
- /// </summary>
- /// <param name="callback">The callback to invoke when the handshake has been received.</param>
- internal void StartWaitingForHandshake(Action<byte[]> callback)
- {
- try
- {
- StartWaitingForHeader(
- delegate (byte[] bytes)
- {
- //Remove version byte
- byte[] dataBytes = new byte[bytes.Length - 1];
- Buffer.BlockCopy(bytes, 1, dataBytes, 0, bytes.Length - 1);
-
- callback.Invoke(dataBytes);
- }
- );
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while initiating the first receive operation.", e));
- }
- }
-
- /// <summary>
- /// Starts this connections waiting for the header.
- /// </summary>
- /// <param name="callback">The callback to invoke when the body has been read.</param>
- void StartWaitingForHeader(Action<byte[]> callback)
- {
- StartWaitingForBytes(4, (bytes) => HeaderReadCallback(bytes, callback));
- }
-
- /// <summary>
- /// Waits for the specified amount of bytes to be received.
- /// </summary>
- /// <param name="length">The number of bytes to receive.</param>
- /// <param name="callback">The callback </param>
- void StartWaitingForBytes(int length, Action<byte[]> callback)
- {
- StateObject state = new StateObject(length, callback);
-
- StartWaitingForChunk(state);
- }
-
- /// <summary>
- /// Waits for the next chunk of data from this socket.
- /// </summary>
- /// <param name="state">The StateObject for the receive operation.</param>
- void StartWaitingForChunk(StateObject state)
- {
- lock (socketLock)
- {
- //Double check we've not disconnected then begin receiving
- if (State == ConnectionState.Connected || State == ConnectionState.Connecting)
- socket.BeginReceive(state.buffer, state.totalBytesReceived, state.buffer.Length - state.totalBytesReceived, SocketFlags.None, ChunkReadCallback, state);
- }
- }
-
- /// <summary>
- /// Called when a chunk has been read.
- /// </summary>
- /// <param name="result"></param>
- void ChunkReadCallback(IAsyncResult result)
- {
- int bytesReceived;
-
- //End the receive operation
- try
- {
- lock (socketLock)
- bytesReceived = socket.EndReceive(result);
- }
- catch (ObjectDisposedException)
- {
- //If the socket's been disposed then we can just end there.
- return;
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while completing a chunk read operation.", e));
- return;
- }
-
- StateObject state = (StateObject)result.AsyncState;
-
- state.totalBytesReceived += bytesReceived; //TODO threading issues on state?
-
- //Exit if receive nothing
- if (bytesReceived == 0)
- {
- HandleDisconnect();
- return;
- }
-
- //If we need to receive more then wait for more, else process it.
- if (state.totalBytesReceived < state.buffer.Length)
- {
- try
- {
- StartWaitingForChunk(state);
- }
- catch (Exception e)
- {
- HandleDisconnect(new HazelException("An exception occured while initiating a chunk receive operation.", e));
- return;
- }
- }
- else
- state.callback.Invoke(state.buffer);
- }
-
- /// <summary>
- /// Called when the socket has been disconnected at the remote host.
- /// </summary>
- /// <param name="e">The exception if one was the cause.</param>
- void HandleDisconnect(HazelException e = null)
- {
- bool invoke = false;
-
- lock (socketLock)
- {
- //Only invoke the disconnected event if we're not already disconnecting
- if (State == ConnectionState.Connected)
- {
- State = ConnectionState.Disconnecting;
- invoke = true;
- }
- }
-
- //Invoke event outide lock if need be
- if (invoke)
- {
- InvokeDisconnected(e);
-
- Dispose();
- }
- }
-
- /// <summary>
- /// Appends the length header to the bytes.
- /// </summary>
- /// <param name="bytes">The source bytes.</param>
- /// <returns>The new bytes.</returns>
- static byte[] AppendLengthHeader(byte[] bytes)
- {
- byte[] fullBytes = new byte[bytes.Length + 4];
-
- //Append length
- fullBytes[0] = (byte)(((uint)bytes.Length >> 24) & 0xFF);
- fullBytes[1] = (byte)(((uint)bytes.Length >> 16) & 0xFF);
- fullBytes[2] = (byte)(((uint)bytes.Length >> 8) & 0xFF);
- fullBytes[3] = (byte)(uint)bytes.Length;
-
- //Add rest of bytes
- Buffer.BlockCopy(bytes, 0, fullBytes, 4, bytes.Length);
-
- return fullBytes;
- }
-
- /// <summary>
- /// Returns the length from a length header.
- /// </summary>
- /// <param name="bytes">The bytes received.</param>
- /// <returns>The number of bytes.</returns>
- static int GetLengthFromBytes(byte[] bytes)
- {
- if (bytes.Length < 4)
- throw new IndexOutOfRangeException("Not enough bytes passed to calculate length.");
-
- return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
- }
-
- /// <inheritdoc />
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- lock (socketLock)
- {
- State = ConnectionState.NotConnected;
-
- if (socket.Connected)
- socket.Shutdown(SocketShutdown.Send);
- socket.Close();
- }
- }
-
- base.Dispose(disposing);
- }
- }
-}
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-
-
-namespace Hazel.Tcp
-{
- /// <summary>
- /// Listens for new TCP connections and creates TCPConnections for them.
- /// </summary>
- /// <inheritdoc />
- public sealed class TcpConnectionListener : NetworkConnectionListener
- {
- /// <summary>
- /// The socket listening for connections.
- /// </summary>
- Socket listener;
-
- /// <summary>
- /// Creates a new TcpConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
- /// </summary>
- /// <param name="IPAddress">The IPAddress to listen on.</param>
- /// <param name="port">The port to listen on.</param>
- /// <param name="mode">The <see cref="IPMode"/> to listen with.</param>
- [Obsolete("Temporary constructor in beta only, use NetworkEndPoint constructor instead.")]
- public TcpConnectionListener(IPAddress IPAddress, int port, IPMode mode = IPMode.IPv4)
- : this (new NetworkEndPoint(IPAddress, port, mode))
- {
-
- }
-
- /// <summary>
- /// Creates a new TcpConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
- /// </summary>
- /// <param name="endPoint">The end point to listen on.</param>
- public TcpConnectionListener(NetworkEndPoint endPoint)
- {
- this.EndPoint = endPoint.EndPoint;
- this.IPMode = endPoint.IPMode;
-
- if (endPoint.IPMode == IPMode.IPv4)
- this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- else
- {
- if (!Socket.OSSupportsIPv6)
- throw new HazelException("IPV6 not supported!");
-
- this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
- this.listener.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
- }
- }
-
- /// <inheritdoc />
- public override void Start()
- {
- try
- {
- lock (listener)
- {
- listener.Bind(EndPoint);
- listener.Listen(1000);
-
- listener.BeginAccept(AcceptConnection, null);
- }
- }
- catch (SocketException e)
- {
- throw new HazelException("Could not start listening as a SocketException occured", e);
- }
- }
-
- /// <summary>
- /// Called when a new connection has been accepted by the listener.
- /// </summary>
- /// <param name="result">The asyncronous operation's result.</param>
- void AcceptConnection(IAsyncResult result)
- {
- lock (listener)
- {
- //Accept Tcp socket
- Socket tcpSocket;
- try
- {
- tcpSocket = listener.EndAccept(result);
- }
- catch (ObjectDisposedException)
- {
- //If the socket's been disposed then we can just end there.
- return;
- }
-
- //Start listening for the next connection
- listener.BeginAccept(new AsyncCallback(AcceptConnection), null);
-
- //Sort the event out
- TcpConnection tcpConnection = new TcpConnection(tcpSocket);
-
- //Wait for handshake
- tcpConnection.StartWaitingForHandshake(
- delegate (byte[] bytes)
- {
- //Invoke
- InvokeNewConnection(bytes, tcpConnection);
-
- tcpConnection.StartReceiving();
- }
- );
- }
- }
-
- /// <inheritdoc/>
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- lock (listener)
- listener.Close();
- }
-
- base.Dispose(disposing);
- }
- }
-}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace Hazel.Udp
+{
+ ///
+ public class BroadcastPacket
+ {
+ ///
+ public string Data;
+
+ ///
+ public DateTime ReceiveTime;
+
+ ///
+ public IPEndPoint Sender;
+
+ ///
+ public BroadcastPacket(string data, IPEndPoint sender)
+ {
+ this.Data = data;
+ this.Sender = sender;
+ this.ReceiveTime = DateTime.Now;
+ }
+
+ public string GetAddress()
+ {
+ return this.Sender.Address.ToString();
+ }
+ }
+
+ ///
+ public class UdpBroadcastListener : IDisposable
+ {
+ private Socket socket;
+ private EndPoint endpoint;
+
+ private byte[] buffer = new byte[1024];
+
+ private List<BroadcastPacket> packets = new List<BroadcastPacket>();
+
+ ///
+ public UdpBroadcastListener(int port)
+ {
+ this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ this.endpoint = new IPEndPoint(IPAddress.Any, port);
+ this.socket.Bind(this.endpoint);
+ }
+
+ ///
+ public void StartListen()
+ {
+ if (this.socket == null) return;
+
+ try
+ {
+ EndPoint endpt = new IPEndPoint(IPAddress.Any, 0);
+ var result = this.socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref endpt, this.HandleData, null);
+ if (result.CompletedSynchronously)
+ {
+ this.HandleData(result);
+ }
+ }
+ catch
+ {
+ this.Dispose();
+ }
+ }
+
+ ///
+ public void HandleData(IAsyncResult result)
+ {
+ int numBytes;
+ EndPoint endpt = new IPEndPoint(IPAddress.Any, 0);
+ try
+ {
+ numBytes = this.socket.EndReceiveFrom(result, ref endpt);
+ }
+ catch
+ {
+ this.Dispose();
+ return;
+ }
+
+ if (numBytes < 2) return;
+ if (buffer[0] != 4 || buffer[1] != 2) return;
+
+ IPEndPoint ipEnd = (IPEndPoint)endpt;
+ string data = ASCIIEncoding.ASCII.GetString(buffer, 2, numBytes - 2);
+ int dataHash = data.GetHashCode();
+
+ lock (packets)
+ {
+ bool found = false;
+ for (int i = 0; i < this.packets.Count; ++i)
+ {
+ var pkt = this.packets[i];
+ if (pkt.Data.GetHashCode() == dataHash
+ && pkt.Sender.Equals(ipEnd))
+ {
+ this.packets[i].ReceiveTime = DateTime.Now;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ this.packets.Add(new BroadcastPacket(data, ipEnd));
+ }
+ }
+
+ this.StartListen();
+ }
+
+ ///
+ public BroadcastPacket[] GetPackets()
+ {
+ lock (this.packets)
+ {
+ var output = this.packets.ToArray();
+ this.packets.Clear();
+ return output;
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ if (this.socket != null)
+ {
+ this.socket.Close();
+ this.socket = null;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace Hazel.Udp
+{
+ ///
+ public class UdpBroadcaster : IDisposable
+ {
+ ///
+ private Socket socket;
+
+ ///
+ private byte[] data;
+
+ ///
+ private EndPoint endpoint;
+
+ ///
+ public UdpBroadcaster(int port)
+ {
+ this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
+ this.endpoint = new IPEndPoint(IPAddress.Broadcast, port);
+ }
+
+ ///
+ public void SetData(string data)
+ {
+ int len = ASCIIEncoding.ASCII.GetByteCount(data);
+ this.data = new byte[len + 2];
+ this.data[0] = 4;
+ this.data[1] = 2;
+
+ ASCIIEncoding.ASCII.GetBytes(data, 0, data.Length, this.data, 2);
+ }
+
+ ///
+ public void Broadcast()
+ {
+ if (this.data == null)
+ {
+ return;
+ }
+
+ this.socket.BeginSendTo(data, 0, data.Length, SocketFlags.None, this.endpoint, (evt) => this.socket.EndSendTo(evt), null);
+ }
+
+ ///
+ public void Dispose()
+ {
+ if (this.socket != null)
+ {
+ this.socket.Close();
+ this.socket = null;
+ }
+ }
+ }
+}
\ No newline at end of file