public override void Receive(byte[] data, int startIndex, int length) { //UnityEngine.Debug.LogError("Receive: " + GetType().ToString() + ": " + DeBox.Teleport.Debugging.TeleportDebugUtils.DebugString(data, startIndex, length)); var expectedDataSize = BitConverter.ToUInt16(data, startIndex); startIndex += sizeof(ushort); length = length - sizeof(ushort); var receivedDataSize = length; if (_receiveLeftoversLength > 0) { Array.Copy(data, startIndex, _receiveLeftovers, _receiveLeftoversLength, receivedDataSize); data = _receiveLeftovers; _receiveLeftoversLength = _receiveLeftoversLength + receivedDataSize; receivedDataSize = _receiveLeftoversLength; UnityEngine.Debug.LogError("Had leftovers"); startIndex = 0; length = _receiveLeftoversLength; } if (expectedDataSize > receivedDataSize) { Array.Copy(data, startIndex, _receiveLeftovers, 0, receivedDataSize); _receiveLeftoversLength = receivedDataSize; UnityEngine.Debug.LogError("Not enough data! expected: " + expectedDataSize + " got: " + receivedDataSize); return; } else if (expectedDataSize < receivedDataSize) { _receiveLeftoversLength = receivedDataSize - expectedDataSize; Array.Copy(data, startIndex + expectedDataSize, _receiveLeftovers, 0, _receiveLeftoversLength); length = expectedDataSize; } InternalChannel.Receive(data, startIndex, length); }
public override byte[] PrepareToSend(byte[] data) { data = InternalChannel.PrepareToSend(data); var header = BitConverter.GetBytes((ushort)data.Length); var fullData = new byte[header.Length + data.Length]; Array.Copy(header, 0, fullData, 0, header.Length); Array.Copy(data, 0, fullData, header.Length, data.Length); //UnityEngine.Debug.LogError("Prepare: " + GetType().ToString() + ": " + DeBox.Teleport.Debugging.TeleportDebugUtils.DebugString(fullData)); return(fullData); }
public override void Upkeep() { InternalChannel.Upkeep(); int maxAcks = 1; ushort sequenceNumber; while (_pendingAcksQueue.Count > 0 && maxAcks > 0) { maxAcks--; sequenceNumber = _pendingAcksQueue.Dequeue(); byte[] ackData = new byte[] { 0xff, 0xff, 0, 0 }; Array.Copy(BitConverter.GetBytes(sequenceNumber), 0, ackData, 2, 2); Send(ackData); } }
private void ProcessInbox() { if (_inbox.Count == 0) { return; } InboxItem inboxItem; ushort nextIndex; while (_lastReceiveIndex > _lastProcessedReceiveIndex) { nextIndex = (ushort)(_lastProcessedReceiveIndex + 1); if (!_inbox.TryGetValue(nextIndex, out inboxItem)) { break; } InternalChannel.Receive(inboxItem.data, inboxItem.startIndex, inboxItem.length); _lastProcessedReceiveIndex = nextIndex; } }
public static void RemoveClientFromChannel(ConnectedClient ws, string channel) { if (!ws.Channels.Contains(channel)) { return; } InternalChannel internalChannel = null; lock (Channels) { if (!Channels.ContainsKey(channel)) { return; } internalChannel = Channels[channel]; } lock (internalChannel.Clients) internalChannel.Clients.Remove(ws); ws.Channels.Remove(channel); }
public override byte[] PrepareToSend(byte[] data) { data = InternalChannel.PrepareToSend(data); byte[] sequenceBytes = BitConverter.GetBytes(_outgoingSequence); var newData = new byte[data.Length + sequenceBytes.Length]; Array.Copy(sequenceBytes, 0, newData, 0, sequenceBytes.Length); Array.Copy(data, 0, newData, sequenceBytes.Length, data.Length); if (_sendAcks) { lock (_outboxLock) { _outbox[_outgoingSequence] = new OutboxItem() { data = newData, nextSendTime = DateTime.UtcNow.Ticks + ACK_TIMEOUT_DURATION_IN_TICKS }; } } _outgoingSequence++; return(newData); }
/// Websocket methods public static async Task AddClientToChannelAsync(ConnectedClient ws, string channel) { if (ws.Channels.Contains(channel)) { return; } IdentityHandler.InternalIdentity identity = null; if (channel.StartsWith("https://") || channel.StartsWith("http://")) { // Check if this client is allowed to join this channel identity = await IdentityHandler.GetIdentityAsync(ws.ClientId, channel); if (identity == null) { return; } } InternalChannel internalChannel = null; lock (Channels) { if (!Channels.ContainsKey(channel)) { Channels.Add(channel, new InternalChannel() { Channel = channel, LastMessage = DateTime.UtcNow, Messages = new ChannelMessageData[MaxMessageBuffer], CurIndex = 0, Clients = new List <ConnectedClient>() }); } internalChannel = Channels[channel]; } lock (internalChannel.Clients) internalChannel.Clients.Add(ws); ws.Channels.Add(channel); }
public override byte[] PrepareToSend(byte[] data) { data = InternalChannel.PrepareToSend(data); byte[] sequenceBytes = BitConverter.GetBytes(_outgoingSequence); var newData = new byte[data.Length + sequenceBytes.Length]; Array.Copy(sequenceBytes, 0, newData, 0, sequenceBytes.Length); Array.Copy(data, 0, newData, sequenceBytes.Length, data.Length); if (_sendAcks) { lock (_outboxLock) { _outbox[_outgoingSequence] = new OutboxItem() { data = newData, lastSendTime = DateTime.UtcNow.Ticks }; } } _outgoingSequence++; //UnityEngine.Debug.LogError("Prepare: " + GetType().ToString() + ": " + DeBox.Teleport.Debugging.TeleportDebugUtils.DebugString(newData)); return(newData); }
private void ProcessOutbox() { if (_outbox.Count == 0 || !_sendAcks) { return; } ushort seqId; OutboxItem outboxItem; lock (_outboxLock) { foreach (var p in _outbox) { seqId = p.Key; outboxItem = p.Value; if (outboxItem.lastSendTime < DateTime.UtcNow.Ticks - 10000000) { outboxItem.lastSendTime = DateTime.UtcNow.Ticks; InternalChannel.Send(outboxItem.data); } } } }
private void ProcessOutbox() { if (_outbox.Count == 0 || !_sendAcks) { return; } ushort seqId; OutboxItem outboxItem; lock (_outboxLock) { foreach (var p in _outbox) { seqId = p.Key; outboxItem = p.Value; if (outboxItem.nextSendTime < DateTime.UtcNow.Ticks) { outboxItem.nextSendTime = DateTime.UtcNow.Ticks + ACK_TIMEOUT_DURATION_IN_TICKS * (1 + (_outbox.Count / ACK_TIMEOUT_INCREMENT_PER_MESSAGE_COUNT)); InternalChannel.Send(outboxItem.data); } } } }
public override void Send(byte[] data) { InternalChannel.Send(data); ProcessOutbox(); }
public static async Task <ChannelData> GetMessagesForChannelSinceIndexAsync(string clientId, string channel, long index) { // If Channel starts with https:// , use the clientId to request the identity from the url IdentityHandler.InternalIdentity identity = null; if (channel.StartsWith("https://") || channel.StartsWith("http://")) { identity = await IdentityHandler.GetIdentityAsync(clientId, channel); if (identity == null) { return(null); } } InternalChannel internalChannel = null; lock (Channels) { if (!Channels.ContainsKey(channel)) { return(null); } internalChannel = Channels[channel]; } var container = new ChannelData() { Channel = channel }; if (identity != null && !identity.Data.allowChannelReceive) // Check if we are allowed to receive messages { container.Messages = new List <ChannelMessageData>(); return(container); } if (index == -1) // On the first request from the client (index == -1), we will only give back at what position we are. { container.Index = internalChannel.CurIndex; container.Messages = new List <ChannelMessageData>(); return(container); } // Find out count to pre-allocate the memory space before going into our locked section var count = internalChannel.CurIndex - index; if (count > MaxMessageBuffer) { count = MaxMessageBuffer; } if (count <= 0) { container.Messages = new List <ChannelMessageData>(); container.Index = internalChannel.CurIndex; return(container); } container.Messages = new List <ChannelMessageData>((int)count); lock (internalChannel.Messages) { // In case the CurIndex is changed before we got the lock container.Index = internalChannel.CurIndex; count = internalChannel.CurIndex - index; if (count > MaxMessageBuffer || count < 0) { count = MaxMessageBuffer; } for (var i = internalChannel.CurIndex - count; i < internalChannel.CurIndex; i++) { var arrayIndex = i % MaxMessageBuffer; var messageObj = internalChannel.Messages[(int)arrayIndex]; if (messageObj.ClientId == clientId) { continue; } container.Messages.Add(messageObj); } } return(container); }
public static async Task SendMessageAsync(string clientId, string channel, string data, ConnectedClient filterClient = null) { if (data.Length > 1024 * 4) { return; } // If Channel starts with https:// , use the clientId to request the identity from the url IdentityHandler.InternalIdentity identity = null; if (channel.StartsWith("https://") || channel.StartsWith("http://")) { identity = await IdentityHandler.GetIdentityAsync(clientId, channel); if (identity == null || !identity.Data.allowChannelSend) { return; } } // Add to channel message hub InternalChannel internalChannel = null; lock (Channels) { if (!Channels.ContainsKey(channel)) { Channels.Add(channel, new InternalChannel() { Channel = channel, LastMessage = DateTime.UtcNow, Messages = new ChannelMessageData[MaxMessageBuffer], CurIndex = 0, Clients = new List <ConnectedClient>() }); } internalChannel = Channels[channel]; } var message = new ChannelMessageData() { Data = data, Identity = identity, ClientId = clientId }; lock (internalChannel.Messages) { var arrayIndex = internalChannel.CurIndex % MaxMessageBuffer; internalChannel.Messages[arrayIndex] = message; internalChannel.CurIndex++; internalChannel.LastMessage = DateTime.UtcNow; } // Broadcast to all websocket connected clients List <ConnectedClient> clients = null; lock (internalChannel.Clients) clients = internalChannel.Clients.Where(a => (filterClient != null && a != filterClient) || (filterClient == null && a.ClientId != clientId)).ToList(); if (clients.Count > 0) { var json = System.Text.Json.JsonSerializer.Serialize(new CommandMessageData() { Channel = channel, Identity = identity?.Data?.publicIdentifier, Data = data }); var dataBytes = System.Text.ASCIIEncoding.UTF8.GetBytes("MSG|" + json.Length + "|" + json); // MSG = 'Channel Message' Command Type foreach (var client in clients) { _ = client.SendDataAsync(dataBytes); } } }