public void Broadcast(ADMMessage message) { if (message == null) { return; } MessageTags.Release(message.Tag); message.Target = null; //clear the target as it may have been used internally but now this is a broadcast so it's not intended for any specific target if (_listener != null && message.CanBroadcast) { switch (message.Type) { case Chetch.Messaging.MessageType.ERROR: _listener(message, this); break; default: if (State == ADMState.DEVICE_READY || State == ADMState.DEVICE_CONNECTED) { _listener(message, this); } break; } } }
/// <summary> /// Since the message objects get modified during processing, we do the chunking synchronously here. /// Otherwise we would end up sending a bunch of extra stuff to the bridge. /// </summary> /// <param name="message"></param> public void SendToBridge(Message message) { MessageTags tags = new MessageTags(); tags.BridgePriority = GetBridgePriority(message); if (tags.BridgePriority == MessagePriority.Lowest) { //These are DeckSlideContentMessages which are big and are unused in the current implementation of the streaming/archiving system. Trace.WriteLine("Client U2M Bridge dropping message:" + message.ToString()); return; } // Serialize the message and split it into chunks with the chunk encoder. // The encoder updates the current message- and chunk sequence numbers. Chunk[] chunks; using (Synchronizer.Lock(this)) { chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence); Trace.WriteLine("Client U2M Bridge MakeChunks returned " + chunks.Length.ToString() + " chunks for message " + message.ToString()); } lock (m_SendQueue) { m_SendQueue.Enqueue(new ChunksAndPriority(chunks, tags)); } m_SendQueueWait.Set(); }
public byte RequestStatus(byte boardID = 0) { var message = new ADMMessage(MessageTags.CreateTag()); message.Type = Messaging.MessageType.STATUS_REQUEST; message.TargetID = boardID; SendMessage(message); return(message.Tag); }
public byte Ping() { var message = new ADMMessage(MessageTags.CreateTag()); message.Type = Messaging.MessageType.PING; message.TargetID = 0; SendMessage(message); return(message.Tag); }
public byte Initialise() { ADMMessage message = new ADMMessage(MessageTags.CreateTag()); message.TargetID = 0; message.Type = Messaging.MessageType.INITIALISE; SendMessage(message); return(message.Tag); }
/// <summary> /// Serialize the chunk then enqueue a send operation for each client socket for which there is a /// participant membership in the receivers group. /// </summary> /// <param name="chunk"></param> /// <param name="receivers"></param> public void Send(Chunk chunk, Group receivers, MessageTags tags) { using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) { stream.Position = 0; this.m_Encoder.EncodeChunk(stream, chunk); byte[] buffer = stream.GetBuffer(); int length = (int)stream.Length; //Unicast to multicast bridge #if RTP_BUILD if ((this.m_U2MBridge != null) && !(receivers is SingletonGroup)) { this.m_U2MBridge.Send(buffer, length, tags); } #endif lock (m_AllClients) { foreach (ClientData client in m_AllClients.Values) { if (client.Participant != null) { if (!client.Participant.Groups.Contains(receivers)) { if (receivers == Group.AllParticipant) { //Should never happen Trace.WriteLine("!!!Participant has lost membership in 'All Participants' (sending anyway). Client= " + client.Id.ToString(), this.GetType().ToString()); if (client.Socket != null) { Trace.WriteLine(" Socket endpoint=" + client.Socket.RemoteEndPoint.ToString()); } } else { //Ignoring participant (no group match) if (receivers is SingletonGroup) { //Trace.WriteLine("Ignoring participant; no group match for singleton group; participant= " + client.Id.ToString(), this.GetType().ToString()); } else { Trace.WriteLine("Ignoring participant; no group match for group: " + receivers.FriendlyName + "; participant= " + client.Id.ToString(), this.GetType().ToString()); } continue; } } } else { //This probably shouldn't ever happen Trace.WriteLine("Failed to find a participant for a connected socket.", this.GetType().ToString()); continue; } m_ServerSender.Send(buffer, length, client, tags, chunk.MessageSequence, chunk.ChunkSequenceInMessage, false); } } } }
/// <summary> /// If connected, send a message, otherwise do nothing. /// </summary> /// <param name="chunk"></param> /// <param name="group"></param> public void Send(Chunk chunk, Group group, MessageTags tags) { AsyncCallback ac = new AsyncCallback(SendCallback); using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) { stream.Position = 0; this.m_Encoder.EncodeChunk(stream, chunk); Trace.WriteLine(string.Format("TCPClient sending message #{0}, chunk #{1} of {2}, {3} bytes.", chunk.MessageSequence, chunk.ChunkSequenceInMessage + 1, chunk.NumberOfChunksInMessage, chunk.Data.Length), this.GetType().ToString()); byte[] buffer = stream.GetBuffer(); int length = (int)stream.Length; if ((m_Socket.Connected) && (m_Connected)) { while (true) { try { m_Socket.BeginSend(buffer, 0, length, SocketFlags.None, ac, new SendState(m_Socket, length)); //BeginSend since it won't block. break; } catch (SocketException se) { if (se.ErrorCode == 10055) { Trace.WriteLine("Client Send queue is full. Sleeping 50 mS", this.GetType().ToString()); Thread.Sleep(50); } else { Trace.WriteLine("SendThread socket exception: " + se.ToString() + " Error code: " + se.ErrorCode.ToString(), this.GetType().ToString()); break; } } catch (ObjectDisposedException ode) { Trace.WriteLine("SendThread ObjectDisposedException: " + ode.Message, this.GetType().ToString()); break; } catch (Exception e) { Trace.WriteLine("SendThread Exception: " + e.ToString(), this.GetType().ToString()); break; } } } else { Trace.WriteLine("SendThread found a socket disconnected. Send request ignored.", this.GetType().ToString()); } } }
/// <summary> /// The main Send method. Called both by the unicast code and as a result of calls from the BeaconService thread. /// </summary> /// <param name="buf"></param> public void Send(byte[] buf, int length, MessageTags tags) { BridgeMessage bmsg = new BridgeMessage(new BufferChunk(buf, 0, length), tags.BridgePriority); if (m_SendQueue != null) { lock (m_SendQueue) { m_SendQueue.Enqueue(bmsg); m_SendQueueWait.Set(); } } }
public SendParameters(byte[] buffer, int length, Guid id, MessageTags tags, ulong messageSequence, ulong chunkSequence, bool isHeartbeat) { this.Buffer = buffer; this.Length = length; this.Id = id; this.Tags = tags; this.SequenceNumber = ulong.MaxValue; this.MessageSequence = messageSequence; this.ChunkSequence = chunkSequence; IsPublicNode = false; this.IsHeartbeatMessage = isHeartbeat; }
/// <summary> /// Enqueue the send to occur when network bandwidth is available. /// </summary> /// <param name="buffer"></param> /// <param name="length"></param> /// <param name="socket"></param> /// <param name="participant"></param> internal void Send(byte[] buffer, int length, ClientData client, MessageTags tags, ulong messageSequence, ulong chunkSequence, bool isHeartbeat) { using (Synchronizer.Lock(m_SocketMap)) { Debug.Assert(m_SocketMap.ContainsKey(client.Id), "TCPServerSender.Send found a missing Socket Map entry."); } //Trace.WriteLine("TCPServerSender.Send seq=" + messageSequence.ToString()); //Enqueue the message using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) { m_SendQueue.Enqueue(new SendParameters(buffer, length, client.Id, tags, messageSequence, chunkSequence, isHeartbeat), client.Participant); // Notify the sender thread that the queue's status has changed. cookie.PulseAll(); } }
public byte SendCommand(byte targetID, ArduinoCommand command, List <Object> extraArgs = null, byte tag = 0) { var message = new ADMMessage(); message.LittleEndian = LittleEndian; message.Type = Messaging.MessageType.COMMAND; message.TargetID = targetID; message.Tag = tag == 0 ? MessageTags.CreateTag() : tag; message.CommandID = command.ID; List <Object> allArgs = command.Arguments; if (extraArgs != null && extraArgs.Count > 0) { allArgs.AddRange(extraArgs); } foreach (Object arg in allArgs) { byte[] b; if (arg is String) { b = Chetch.Utilities.Convert.ToBytes((String)arg); } else if (arg.GetType().IsValueType) { b = Chetch.Utilities.Convert.ToBytes((ValueType)arg, LittleEndian); } else { throw new Exception("Unable to process type " + arg.GetType()); } message.AddArgument(b); } return(SendMessage(message)); }
/// <summary> /// Since the message objects get modified during processing, we do the chunking synchronously here. /// Otherwise we would end up sending a bunch of extra stuff to the bridge. /// </summary> /// <param name="message"></param> public void SendToBridge(Message message) { MessageTags tags = new MessageTags(); tags.BridgePriority = GetBridgePriority(message); if (tags.BridgePriority == MessagePriority.Lowest) { //These are DeckSlideContentMessages which are big and are unused in the current implementation of the streaming/archiving system. Trace.WriteLine("Client U2M Bridge dropping message:" + message.ToString()); return; } // Serialize the message and split it into chunks with the chunk encoder. // The encoder updates the current message- and chunk sequence numbers. Chunk[] chunks; using (Synchronizer.Lock(this)) { chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence); Trace.WriteLine("Client U2M Bridge MakeChunks returned " + chunks.Length.ToString() + " chunks for message " + message.ToString()); } lock (m_SendQueue) { m_SendQueue.Enqueue(new ChunksAndPriority(chunks, tags)); } m_SendQueueWait.Set(); }
/// <summary> /// The override of SendingQueue.Send used by the BeaconService. Chunk, serialize and pass to the main send method. /// </summary> /// <param name="message"></param> /// <param name="priority"></param> public override void Send(Message message, UW.ClassroomPresenter.Model.MessagePriority priority) { Group receivers = message.Group != null ? message.Group : Group.AllParticipant; Chunk[] chunks; // Serialize the message and split it into chunks with the chunk encoder. // The encoder updates the current message- and chunk sequence numbers. using (Synchronizer.Lock(this)) { chunks = this.m_Encoder.MakeChunks(message, ref this.m_ChunkSequence); } foreach (Chunk c in chunks) { using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) { stream.Position = 0; this.m_Encoder.EncodeChunk(stream, c); byte[] buffer = stream.GetBuffer(); int length = (int)stream.Length; MessageTags tags = new MessageTags(); tags.BridgePriority = MessagePriority.Higher; this.Send(buffer, length, tags); } } }
/// <summary> /// Serialize the chunk then enqueue a send operation for each client socket for which there is a /// participant membership in the receivers group. /// </summary> /// <param name="chunk"></param> /// <param name="receivers"></param> public void Send(Chunk chunk, Group receivers, MessageTags tags) { using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) { stream.Position = 0; this.m_Encoder.EncodeChunk(stream, chunk); byte[] buffer = stream.GetBuffer(); int length = (int)stream.Length; //Unicast to multicast bridge #if RTP_BUILD if ((this.m_U2MBridge != null) && !(receivers is SingletonGroup)) { this.m_U2MBridge.Send(buffer,length,tags); } #endif lock (m_AllClients) { foreach (ClientData client in m_AllClients.Values) { if (client.Participant != null) { if (!client.Participant.Groups.Contains(receivers)) { if (receivers == Group.AllParticipant) { //Should never happen Trace.WriteLine("!!!Participant has lost membership in 'All Participants' (sending anyway). Client= " + client.Id.ToString(), this.GetType().ToString()); if (client.Socket != null) Trace.WriteLine(" Socket endpoint=" + client.Socket.RemoteEndPoint.ToString()); } else { //Ignoring participant (no group match) if (receivers is SingletonGroup) { //Trace.WriteLine("Ignoring participant; no group match for singleton group; participant= " + client.Id.ToString(), this.GetType().ToString()); } else Trace.WriteLine("Ignoring participant; no group match for group: " + receivers.FriendlyName + "; participant= " + client.Id.ToString(), this.GetType().ToString()); continue; } } } else { //This probably shouldn't ever happen Trace.WriteLine("Failed to find a participant for a connected socket.", this.GetType().ToString()); continue; } m_ServerSender.Send(buffer, length, client, tags, chunk.MessageSequence, chunk.ChunkSequenceInMessage,false); } } } }
public SendParameters(byte[] buffer, int length, Guid id, MessageTags tags, ulong messageSequence, ulong chunkSequence, bool isHeartbeat) { this.Buffer = buffer; this.Length = length; this.Id = id; this.Tags = tags; this.SequenceNumber = ulong.MaxValue; this.MessageSequence = messageSequence; this.ChunkSequence = chunkSequence; IsPublicNode = false; this.IsHeartbeatMessage = isHeartbeat; }
public ChunksAndPriority(Chunk[] chunks, MessageTags tags) { this.Chunks = chunks; this.Tags = tags; }
/// <summary> /// Close disconnected client sockets, send heartbeat messages and do other assorted cleanup. /// </summary> private void MaintenanceThread() { //Prepare the heartbeat message TCPHeartbeatMessage heartbeat = new TCPHeartbeatMessage(); BinaryFormatter bf = new BinaryFormatter(); MemoryStream hbms = new MemoryStream(); bf.Serialize(hbms, heartbeat); byte[] hbbuf = hbms.GetBuffer(); int hblen = (int)hbms.Length; MessageTags hbtags = new MessageTags(); //Lists for socket maintenance List<Guid> toCleanup = new List<Guid>(); List<Socket> toClose = new List<Socket>(); int secondCounter = 0; int connectedClientCount = 0; while (!this.m_Disposed) { //iterate approximately once per second for (int i = 0; ((i < 10) && (!m_Disposed)); i++) { Thread.Sleep(100); } if (this.m_Disposed) break; secondCounter++; lock (m_AllClients) { //Close and remove disconnected sockets. toCleanup.Clear(); toClose.Clear(); connectedClientCount = 0; foreach (ClientData client in m_AllClients.Values) { if (client.ConnectionState == ConnectionState.Disconnected) { //Check for disconnected clients that have timed out if (client.Timeout < DateTime.Now) { this.m_ServerSender.RemoveQueue(client.Id); toCleanup.Add(client.Id); } } else if (client.ConnectionState == ConnectionState.Connected) { //Find sockets to close for any new disconnects if (!client.Socket.Connected) { toClose.Add(client.Socket); client.Socket = null; client.ConnectionState = ConnectionState.Disconnected; client.Timeout = DateTime.Now + TimeSpan.FromMinutes(m_QueueTimeout); } else { connectedClientCount++; //Send a heartbeat message to connected clients if (secondCounter % HEARTBEAT_PERIOD == 0) { //Trace.WriteLine("TCPServer sending Heartbeat to client ID " + client.Id.ToString(), this.GetType().ToString()); this.m_ServerSender.Send(hbbuf, hblen, client, hbtags,0,0,true); } } } else if (client.ConnectionState == ConnectionState.Reconnecting) { //This is a temporary state used during handshaking connectedClientCount++; } } //Remove timed out clients from the master list foreach (Guid goner in toCleanup) { m_AllClients.Remove(goner); } } //Update published client count if (m_ClientCount != connectedClientCount) { using (Synchronizer.Lock(this.SyncRoot)) { this.SetPublishedProperty("ClientCount", ref m_ClientCount, connectedClientCount); NetworkStatus newStatus = m_NetworkStatus.Clone(); newStatus.ClientCount = m_ClientCount; this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus); } } //Close sockets foreach (Socket s in toClose) { try { Trace.WriteLine("MaintenanceThread closing disconnected socket: " + s.RemoteEndPoint.ToString(), this.GetType().ToString()); s.Close(); } catch (Exception e) { Trace.WriteLine("MaintenanceThread exception while closing socket: " + e.ToString(), this.GetType().ToString()); } } if (toCleanup.Count > 0) Trace.WriteLine("MaintenanceThread timed out " + toCleanup.Count.ToString() + " disconnected socket(s).", this.GetType().ToString()); //Print a heartbeat to the log just to make it easier to parse by eye. if (secondCounter%10 == 0) { Trace.WriteLine(DateTime.Now.ToString(), this.GetType().ToString()); } } Trace.WriteLine("MaintenanceThread is ending.", this.GetType().ToString()); }
public void AddMessageTag(MessageTag messageTag) => MessageTags.Add(messageTag);
public byte SendMessage(ADMMessage message) { if (message == null) { return(0); } if (Monitor.TryEnter(SendMessageLock, SEND_MESSAGE_LOCK_TIMEOUT)) { try { bool ready2send; long msSinceLastSent = -1; //loop waiting for a message response do { msSinceLastSent = (DateTime.Now.Ticks - LastMessageSentOn.Ticks) / TimeSpan.TicksPerMillisecond; if (LastMessageSent == null || MessageReceivedSuccess) { ready2send = true; } else { ready2send = msSinceLastSent > MESSAGE_RECEIVED_TIMEOUT; if (!ready2send) { _sleep(10); //crappy idea to reduce load on cpu } else { //so here we have waited long enough for the previous sent message which has failed to arrive //so we proceed with sending anyway Tracing?.TraceEvent(TraceEventType.Warning, 0, "SendMessage {0}: Sending {1} timed out waiting to receive message {2} tag {3}", PortAndNodeID, message.Type, LastMessageSent.Type, LastMessageSent.Tag); } } } while (!ready2send); //store old values before sending (rollback if exception thrown) //This unusual approach is because it appeared like messages were being received before the SendString could exit ADMMessage lastMessageSent = LastMessageSent; DateTime lastMessageSentOn = LastMessageSentOn; try { lock (MessageStatsLock) { MessagesSent++; LastMessageSent = message; LastMessageSentOn = DateTime.Now; MessageReceivedSuccess = false; } if (message.Tag == 0) { message.Tag = MessageTags.CreateTag(); } message.SenderID = BoardID; //Console.WriteLine("-------------> {0}: Sending message {1} tag {2} target {3}." , PortAndNodeID, message.Type, message.Tag, message.TargetID); SendString(message.Serialize()); } catch (Exception e) { lock (MessageStatsLock) //rollback { MessagesSent--; LastMessageSent = lastMessageSent; LastMessageSentOn = lastMessageSentOn; } String errMsg = String.Format("{0} SendMessage: sending {1} (Tag={2}) produced exception {3} {4}", PortAndNodeID, message.Type, message.Tag, e.GetType(), e.Message); Tracing?.TraceEvent(TraceEventType.Error, 0, errMsg); throw e; } } finally { Monitor.Exit(SendMessageLock); } } //end attempt to try get lock else { //we waited too long to get the lock... throw new SendFailedException("ArduinoDeviceManager::SendMessage ... waited to long to obtain lock"); } return(message.Tag); }
public void RemoveMessageTag(MessageTag messageTag) => MessageTags.Remove(messageTag);
public ChunksAndPriority(Chunk[] chunks, MessageTags tags) { this.Chunks = chunks; this.Tags = tags; }
/// <summary> /// If connected, send a message, otherwise do nothing. /// </summary> /// <param name="chunk"></param> /// <param name="group"></param> public void Send(Chunk chunk, Group group, MessageTags tags) { AsyncCallback ac = new AsyncCallback(SendCallback); using (MemoryStream stream = new MemoryStream((int)(this.m_Encoder.MaximumChunkSize * 2))) { stream.Position = 0; this.m_Encoder.EncodeChunk(stream, chunk); Trace.WriteLine(string.Format("TCPClient sending message #{0}, chunk #{1} of {2}, {3} bytes.", chunk.MessageSequence, chunk.ChunkSequenceInMessage + 1, chunk.NumberOfChunksInMessage, chunk.Data.Length), this.GetType().ToString()); byte[] buffer = stream.GetBuffer(); int length = (int)stream.Length; if ((m_Socket.Connected) && (m_Connected)) while (true) { try { m_Socket.BeginSend(buffer, 0, length, SocketFlags.None, ac, new SendState(m_Socket, length)); //BeginSend since it won't block. break; } catch (SocketException se) { if (se.ErrorCode == 10055) { Trace.WriteLine("Client Send queue is full. Sleeping 50 mS", this.GetType().ToString()); Thread.Sleep(50); } else { Trace.WriteLine("SendThread socket exception: " + se.ToString() + " Error code: " + se.ErrorCode.ToString(), this.GetType().ToString()); break; } } catch (ObjectDisposedException ode) { Trace.WriteLine("SendThread ObjectDisposedException: " + ode.Message, this.GetType().ToString()); break; } catch (Exception e) { Trace.WriteLine("SendThread Exception: " + e.ToString(), this.GetType().ToString()); break; } } else { Trace.WriteLine("SendThread found a socket disconnected. Send request ignored.", this.GetType().ToString()); } } }
/// <summary> /// Close disconnected client sockets, send heartbeat messages and do other assorted cleanup. /// </summary> private void MaintenanceThread() { //Prepare the heartbeat message TCPHeartbeatMessage heartbeat = new TCPHeartbeatMessage(); BinaryFormatter bf = new BinaryFormatter(); MemoryStream hbms = new MemoryStream(); #if GENERIC_SERIALIZATION heartbeat.Serialize().WriteToStream(hbms); #else bf.Serialize(hbms, heartbeat); #endif byte[] hbbuf = hbms.GetBuffer(); int hblen = (int)hbms.Length; MessageTags hbtags = new MessageTags(); //Lists for socket maintenance List <Guid> toCleanup = new List <Guid>(); List <Socket> toClose = new List <Socket>(); int secondCounter = 0; int connectedClientCount = 0; while (!this.m_Disposed) { //iterate approximately once per second for (int i = 0; ((i < 10) && (!m_Disposed)); i++) { Thread.Sleep(100); } if (this.m_Disposed) { break; } secondCounter++; lock (m_AllClients) { //Close and remove disconnected sockets. toCleanup.Clear(); toClose.Clear(); connectedClientCount = 0; foreach (ClientData client in m_AllClients.Values) { if (client.ConnectionState == ConnectionState.Disconnected) { //Check for disconnected clients that have timed out if (client.Timeout < DateTime.Now) { this.m_ServerSender.RemoveQueue(client.Id); toCleanup.Add(client.Id); } } else if (client.ConnectionState == ConnectionState.Connected) { //Find sockets to close for any new disconnects if (!client.Socket.Connected) { toClose.Add(client.Socket); client.Socket = null; client.ConnectionState = ConnectionState.Disconnected; client.Timeout = DateTime.Now + TimeSpan.FromMinutes(m_QueueTimeout); } else { connectedClientCount++; //Send a heartbeat message to connected clients if (secondCounter % HEARTBEAT_PERIOD == 0) { //Trace.WriteLine("TCPServer sending Heartbeat to client ID " + client.Id.ToString(), this.GetType().ToString()); this.m_ServerSender.Send(hbbuf, hblen, client, hbtags, 0, 0, true); } } } else if (client.ConnectionState == ConnectionState.Reconnecting) { //This is a temporary state used during handshaking connectedClientCount++; } } //Remove timed out clients from the master list foreach (Guid goner in toCleanup) { m_AllClients.Remove(goner); } } //Update published client count if (m_ClientCount != connectedClientCount) { using (Synchronizer.Lock(this.SyncRoot)) { this.SetPublishedProperty("ClientCount", ref m_ClientCount, connectedClientCount); NetworkStatus newStatus = m_NetworkStatus.Clone(); newStatus.ClientCount = m_ClientCount; this.SetPublishedProperty("NetworkStatus", ref m_NetworkStatus, newStatus); } } //Close sockets foreach (Socket s in toClose) { try { Trace.WriteLine("MaintenanceThread closing disconnected socket: " + s.RemoteEndPoint.ToString(), this.GetType().ToString()); s.Close(); } catch (Exception e) { Trace.WriteLine("MaintenanceThread exception while closing socket: " + e.ToString(), this.GetType().ToString()); } } if (toCleanup.Count > 0) { Trace.WriteLine("MaintenanceThread timed out " + toCleanup.Count.ToString() + " disconnected socket(s).", this.GetType().ToString()); } //Print a heartbeat to the log just to make it easier to parse by eye. if (secondCounter % 10 == 0) { Trace.WriteLine(DateTime.Now.ToString(), this.GetType().ToString()); } } Trace.WriteLine("MaintenanceThread is ending.", this.GetType().ToString()); }
/// <summary> /// Enqueue the send to occur when network bandwidth is available. /// </summary> /// <param name="buffer"></param> /// <param name="length"></param> /// <param name="socket"></param> /// <param name="participant"></param> internal void Send(byte[] buffer, int length, ClientData client, MessageTags tags, ulong messageSequence, ulong chunkSequence, bool isHeartbeat) { using (Synchronizer.Lock(m_SocketMap)) { Debug.Assert(m_SocketMap.ContainsKey(client.Id), "TCPServerSender.Send found a missing Socket Map entry."); } //Trace.WriteLine("TCPServerSender.Send seq=" + messageSequence.ToString()); //Enqueue the message using (Synchronizer.Cookie cookie = Synchronizer.Lock(m_SendQueue)) { m_SendQueue.Enqueue(new SendParameters(buffer, length, client.Id, tags, messageSequence, chunkSequence, isHeartbeat), client.Participant); // Notify the sender thread that the queue's status has changed. cookie.PulseAll(); } }