From: Forest Date: Mon, 16 Jul 2018 19:12:53 +0000 (-0700) Subject: Update to VS2017 and remove TCP and add UDP Broadcaster X-Git-Tag: 1.0.0~90 X-Git-Url: https://git.deb.at/?a=commitdiff_plain;h=4595c60e803ebad09f192d7db650f72e0e5c2cbf;p=rhonda%2Fimpostor.hazel.git Update to VS2017 and remove TCP and add UDP Broadcaster --- diff --git a/.gitignore b/.gitignore index 360e580..9bf6cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.suo *.user *.sln.docstates +.vs/ # Build results [Dd]ebug/ diff --git a/Hazel.UnitTests/BroadcastTests.cs b/Hazel.UnitTests/BroadcastTests.cs new file mode 100644 index 0000000..53b3139 --- /dev/null +++ b/Hazel.UnitTests/BroadcastTests.cs @@ -0,0 +1,39 @@ +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); + } + } + } +} diff --git a/Hazel.UnitTests/Hazel.UnitTests.csproj b/Hazel.UnitTests/Hazel.UnitTests.csproj index a380240..73dbb50 100644 --- a/Hazel.UnitTests/Hazel.UnitTests.csproj +++ b/Hazel.UnitTests/Hazel.UnitTests.csproj @@ -53,10 +53,10 @@ + - diff --git a/Hazel.UnitTests/TcpConnectionTests.cs b/Hazel.UnitTests/TcpConnectionTests.cs deleted file mode 100644 index 9ef0772..0000000 --- a/Hazel.UnitTests/TcpConnectionTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -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 - { - /// - /// Tests the fields on TcpConnection. - /// - [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 }); - } - } - - /// - /// Tests IPv4 connectivity. - /// - [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(); - } - } - - /// - /// Tests dual mode connectivity. - /// - [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(); - } - } - } - - /// - /// Tests sending and receiving on the TcpConnection. - /// - [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); - } - } - - /// - /// Tests sending and receiving on the TcpConnection. - /// - [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); - } - } - - /// - /// Tests disconnection from the client. - /// - [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); - } - } - - /// - /// Tests disconnection from the server. - /// - [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); - } - } - } -} diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index 5cf54e2..98ec8da 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -66,10 +66,9 @@ - - - + + Code diff --git a/Hazel/Tcp/StateObject.cs b/Hazel/Tcp/StateObject.cs deleted file mode 100644 index 8c4ac20..0000000 --- a/Hazel/Tcp/StateObject.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Hazel.Tcp -{ - /// - /// Represents the state of the current receive operation for TCP connections. - /// - struct StateObject - { - /// - /// The buffer we're receiving. - /// - internal byte[] buffer; - - /// - /// The total number of bytes received so far. - /// - internal int totalBytesReceived; - - /// - /// The callback to invoke once the buffer has been filled. - /// - internal Action callback; - - /// - /// Creates a StateObject with the specified length. - /// - /// The number of bytes expected to be received. - /// The callback to invoke once data has been received. - internal StateObject(int length, Action callback) - { - this.buffer = new byte[length]; - this.totalBytesReceived = 0; - this.callback = callback; - } - } -} diff --git a/Hazel/Tcp/TcpConnection.cs b/Hazel/Tcp/TcpConnection.cs deleted file mode 100644 index 99d60c1..0000000 --- a/Hazel/Tcp/TcpConnection.cs +++ /dev/null @@ -1,413 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Hazel.Tcp -{ - /// - /// Represents a connection that uses the TCP protocol. - /// - /// - public sealed class TcpConnection : NetworkConnection - { - /// - /// The socket we're managing. - /// - Socket socket; - - /// - /// Lock for the socket. - /// - Object socketLock = new Object(); - - /// - /// Creates a TcpConnection from a given TCP Socket. - /// - /// The TCP socket to wrap. - 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; - } - } - - /// - /// Creates a new TCP connection. - /// - /// A to connect to. - 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; - } - } - - /// - 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); - } - } - - /// - /// - /// - /// - /// The sendOption parameter is ignored by the TcpConnection as TCP only supports FragmentedReliable - /// communication, specifying anything else will have no effect. - /// - /// - 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); - } - - /// - /// Called when a 4 byte header has been received. - /// - /// The 4 header bytes read. - /// The callback to invoke when the body has been received. - void HeaderReadCallback(byte[] bytes, Action 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)); - } - } - - /// - /// Callback for when a body has been read. - /// - /// The data bytes received by the connection. - 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); - } - - /// - /// Starts this connection receiving data. - /// - internal void StartReceiving() - { - try - { - StartWaitingForHeader(BodyReadCallback); - } - catch (Exception e) - { - HandleDisconnect(new HazelException("An exception occured while initiating the first receive operation.", e)); - } - } - - /// - /// Starts waiting for a first handshake packet to be received. - /// - /// The callback to invoke when the handshake has been received. - internal void StartWaitingForHandshake(Action 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)); - } - } - - /// - /// Starts this connections waiting for the header. - /// - /// The callback to invoke when the body has been read. - void StartWaitingForHeader(Action callback) - { - StartWaitingForBytes(4, (bytes) => HeaderReadCallback(bytes, callback)); - } - - /// - /// Waits for the specified amount of bytes to be received. - /// - /// The number of bytes to receive. - /// The callback - void StartWaitingForBytes(int length, Action callback) - { - StateObject state = new StateObject(length, callback); - - StartWaitingForChunk(state); - } - - /// - /// Waits for the next chunk of data from this socket. - /// - /// The StateObject for the receive operation. - 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); - } - } - - /// - /// Called when a chunk has been read. - /// - /// - 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); - } - - /// - /// Called when the socket has been disconnected at the remote host. - /// - /// The exception if one was the cause. - 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(); - } - } - - /// - /// Appends the length header to the bytes. - /// - /// The source bytes. - /// The new bytes. - 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; - } - - /// - /// Returns the length from a length header. - /// - /// The bytes received. - /// The number of bytes. - 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]; - } - - /// - 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); - } - } -} diff --git a/Hazel/Tcp/TcpConnectionListener.cs b/Hazel/Tcp/TcpConnectionListener.cs deleted file mode 100644 index 8374d20..0000000 --- a/Hazel/Tcp/TcpConnectionListener.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; - - -namespace Hazel.Tcp -{ - /// - /// Listens for new TCP connections and creates TCPConnections for them. - /// - /// - public sealed class TcpConnectionListener : NetworkConnectionListener - { - /// - /// The socket listening for connections. - /// - Socket listener; - - /// - /// Creates a new TcpConnectionListener for the given , port and . - /// - /// The IPAddress to listen on. - /// The port to listen on. - /// The to listen with. - [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)) - { - - } - - /// - /// Creates a new TcpConnectionListener for the given , port and . - /// - /// The end point to listen on. - 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); - } - } - - /// - 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); - } - } - - /// - /// Called when a new connection has been accepted by the listener. - /// - /// The asyncronous operation's result. - 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(); - } - ); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - lock (listener) - listener.Close(); - } - - base.Dispose(disposing); - } - } -} diff --git a/Hazel/Udp/UdpBroadcastListener.cs b/Hazel/Udp/UdpBroadcastListener.cs new file mode 100644 index 0000000..a9b1828 --- /dev/null +++ b/Hazel/Udp/UdpBroadcastListener.cs @@ -0,0 +1,139 @@ +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 packets = new List(); + + /// + 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 diff --git a/Hazel/Udp/UdpBroadcaster.cs b/Hazel/Udp/UdpBroadcaster.cs new file mode 100644 index 0000000..872e685 --- /dev/null +++ b/Hazel/Udp/UdpBroadcaster.cs @@ -0,0 +1,60 @@ +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