public void UdpBroadcastMessage_Serialize() { // Verify that we can serialize and deserialize a message. UdpBroadcastMessage msg; byte[] buffer; IPAddress address = Helper.ParseIPAddress("10.1.2.3"); msg = new UdpBroadcastMessage(UdpBroadcastMessageType.ClientRegister, address, 5, new byte[] { 0, 1, 2, 3, 4 }); msg.TimeStampUtc = new DateTime(2010, 3, 19); Assert.AreEqual(UdpBroadcastMessageType.ClientRegister, msg.MessageType); Assert.AreEqual(5, msg.BroadcastGroup); CollectionAssert.AreEqual(new byte[] { 0, 1, 2, 3, 4 }, msg.Payload); Assert.AreEqual(address, msg.SourceAddress); buffer = msg.ToArray(sharedKey); msg = new UdpBroadcastMessage(buffer, sharedKey); Assert.AreEqual(UdpBroadcastMessageType.ClientRegister, msg.MessageType); Assert.AreEqual(new DateTime(2010, 3, 19), msg.TimeStampUtc); Assert.AreEqual(5, msg.BroadcastGroup); CollectionAssert.AreEqual(new byte[] { 0, 1, 2, 3, 4 }, msg.Payload); Assert.AreEqual(address, msg.SourceAddress); // Verify that the message envelope size constant is correct. Assert.IsTrue(UdpBroadcastMessage.EnvelopeSize >= buffer.Length - msg.Payload.Length); }
public void UdpBroadcast_MessageNulls() { UdpBroadcastMessage msg; msg = new UdpBroadcastMessage(UdpBroadcastMessageType.ServerRegister, 10); Assert.AreEqual(10, msg.BroadcastGroup); CollectionAssert.AreEqual(new byte[0], msg.Payload); }
/// <summary> /// Constructs a <see cref="UdpBroadcastMessage"/> from the parameters passed and /// serializes it into the wire format. /// </summary> /// <param name="messageType">The message type.</param> /// <param name="sourceAddress">The IP address of the message source.</param> /// <param name="broadcastGroup">The broadcast group.</param> /// <param name="payload">The broadcast packet payload.</param> /// <returns>The packet bytes.</returns> private byte[] GetMessageBytes(UdpBroadcastMessageType messageType, IPAddress sourceAddress, int broadcastGroup, byte[] payload) { var message = new UdpBroadcastMessage(messageType, sourceAddress, broadcastGroup, payload); if (FixedTimestampUtc > DateTime.MinValue) { message.TimeStampUtc = FixedTimestampUtc; } return(message.ToArray(settings.SharedKey)); }
/// <summary> /// Called when a packet is received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnReceive(IAsyncResult ar) { UdpBroadcastMessage message = null; IPEndPoint recvEP = null; int cbRecv = 0; lock (syncLock) { if (socket == null) { return; // Client is closed } try { // Parse received packet. cbRecv = socket.EndReceiveFrom(ar, ref rawRecvEP); recvEP = (IPEndPoint)rawRecvEP; if (cbRecv == 0) { return; // This happens when we receive an ICMP(connection-reset) from a } // remote host that's actively refusing an earlier packet transmission. // We're just going to ignore this. perf.TotalMessageRate.Increment(); perf.TotalByteRate.IncrementBy(cbRecv); message = new UdpBroadcastMessage(Helper.Extract(recvBuf, 0, cbRecv), settings.SharedKey); // Validate that the message timestamp is reasonable and discard // messages with timestamps from too far in the past or too far // in the future. DateTime now = DateTime.UtcNow; if (!Helper.Within(now, message.TimeStampUtc, settings.MessageTTL)) { SysLog.LogWarning("UDP Broadcast message timestamp out of range. SystemTime={0}, Timestamp={1}, Source={2}, BroadcastGroup={3}", now.ToString("u"), message.TimeStampUtc.ToString("u"), recvEP, message.BroadcastGroup); return; } } catch (Exception e) { SysLog.LogException(e); } finally { // Initiate the next receive. try { rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); } catch (Exception e) { SysLog.LogException(e); } } // Process the message (if any). if (message == null || PauseNetwork) { return; } ServerState server; ClientState client; switch (message.MessageType) { case UdpBroadcastMessageType.ServerRegister: // Add the server to the tracking table if it's not already // present and update its TTD. if (!servers.TryGetValue(recvEP, out server)) { server = new ServerState(recvEP, SysTime.Now + settings.ServerTTL); servers.Add(recvEP, server); } else { server.TTD = SysTime.Now + settings.ServerTTL; } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ServerUnregister: // Remove the server from the tracking table (if present). if (servers.ContainsKey(recvEP)) { servers.Remove(recvEP); } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ClientRegister: // Add the client to the tracking table if it's not already // present and update its TTD. if (!clients.TryGetValue(recvEP, out client)) { client = new ClientState(recvEP, message.BroadcastGroup, SysTime.Now + settings.ServerTTL); clients.Add(recvEP, client); } else { client.TTD = SysTime.Now + settings.ServerTTL; } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.ClientUnregister: // Remove the client from the tracking table (if present). if (clients.ContainsKey(recvEP)) { clients.Remove(recvEP); } perf.AdminByteRate.IncrementBy(cbRecv); perf.AdminMessageRate.Increment(); break; case UdpBroadcastMessageType.Broadcast: // Transmit the message to all clients the belong to the same broadcast group, // if this is the master server. if (!IsMaster) { return; } var packet = GetMessageBytes(UdpBroadcastMessageType.Broadcast, message.SourceAddress, message.BroadcastGroup, message.Payload); int cDelivered = 0; foreach (var c in clients.Values) { if (c.BroadcastGroup == message.BroadcastGroup) { socket.SendTo(packet, c.EndPoint); cDelivered++; } } perf.BroadcastReceiveByteRate.IncrementBy(cbRecv); perf.BroadcastReceiveMessageRate.Increment(); perf.BroadcastSendByteRate.IncrementBy(cbRecv * packet.Length); perf.BroadcastSendMessageRate.IncrementBy(cDelivered); break; } } }
/// <summary> /// Called when a packet is received on the socket. /// </summary> /// <param name="ar">The async result.</param> private void OnReceive(IAsyncResult ar) { UdpBroadcastMessage message = null; IPEndPoint recvEP; int cbRecv; lock (syncLock) { if (socket == null) { return; // Client is closed } try { // Parse received packet. cbRecv = socket.EndReceiveFrom(ar, ref rawRecvEP); recvEP = (IPEndPoint)rawRecvEP; if (cbRecv == 0) { return; // This happens when we receive an ICMP(connection-reset) from a } // remote host that's actively refusing an earlier packet transmission. // We're just going to ignore this. message = new UdpBroadcastMessage(Helper.Extract(recvBuf, 0, cbRecv), settings.SharedKey); // Validate that the message timestamp is reasonable and discard // messages with timestamps from too far in the past or too far // in the future. DateTime now = DateTime.UtcNow; if (!Helper.Within(now, message.TimeStampUtc, settings.MessageTTL)) { SysLog.LogWarning("UDP Broadcast message timestamp out of range. SystemTime={0}, Timestamp={1}, Source={2}, BroadcastGroup={3}", now.ToString("u"), message.TimeStampUtc.ToString("u"), recvEP, message.BroadcastGroup); return; } } catch (Exception e) { SysLog.LogException(e); } finally { // Initiate the next receive. try { rawRecvEP = new IPEndPoint(IPAddress.Any, 0); socket.BeginReceiveFrom(recvBuf, 0, recvBuf.Length, SocketFlags.None, ref rawRecvEP, onReceive, null); } catch (Exception e) { SysLog.LogException(e); } } } // Process the message (if any) outside of the lock. Note that only // broadcast messages are processed by UDP broadcast clients. if (message == null || message.MessageType != UdpBroadcastMessageType.Broadcast || PauseNetwork) { return; } // Ignore messages that don't match the broadcast group. if (settings.BroadcastGroup != message.BroadcastGroup) { return; } if (PacketReceived != null) { PacketReceived(this, new UdpBroadcastEventArgs(message.SourceAddress, message.Payload)); } }