internal override void Send(ArraySegment <byte> segment, int channelId = Channels.DefaultReliable) { if (logger.LogEnabled()) { logger.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); } //Debug.Log("ConnectionSend " + this + " bytes:" + BitConverter.ToString(segment.Array, segment.Offset, segment.Count)); // validate packet size first. if (ValidatePacketSize(segment, channelId)) { // batching? then add to queued messages if (batching) { // put into a (pooled) writer // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // -> will be returned to pool when sending! PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // add to batch queue Batch batch = GetBatchForChannelId(channelId); batch.messages.Enqueue(writer); } // otherwise send directly to minimize latency else { Transport.activeTransport.ServerSend(connectionId, channelId, segment); } } }
// add a new batch. // returns true if valid. // returns false if not, in which case the connection should be disconnected. public bool AddBatch(ArraySegment <byte> batch) { // IMPORTANT: ArraySegment is only valid until returning. we copy it! // // NOTE: it's not possible to create empty ArraySegments, so we // don't need to check against that. // make sure we have at least 8 bytes to read for tick timestamp if (batch.Count < Batcher.HeaderSize) { return(false); } // put into a (pooled) writer // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // -> will be returned to pool when sending! PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(batch.Array, batch.Offset, batch.Count); // first batch? then point reader there if (batches.Count == 0) { StartReadingBatch(writer); } // add batch batches.Enqueue(writer); //Debug.Log($"Adding Batch {BitConverter.ToString(batch.Array, batch.Offset, batch.Count)} => batches={batches.Count} reader={reader}"); return(true); }
// send a batch. internal so we can test it. internal void SendBatch(int channelId, Batch batch) { // get max batch size for this channel int max = Transport.activeTransport.GetMaxBatchSize(channelId); // we need a writer to merge queued messages into a batch using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // for each queued message while (batch.messages.Count > 0) { // get it PooledNetworkWriter message = batch.messages.Dequeue(); ArraySegment <byte> segment = message.ToArraySegment(); // IF adding to writer would end Up >= MTU then we should // flush first. the goal is to always flush < MTU packets. // // IMPORTANT: if writer is empty and segment is > MTU // (which can happen for large max sized message) // then we would send an empty previous writer. // => don't do that. // => only send if not empty. if (writer.Position > 0 && writer.Position + segment.Count >= max) { // flush & reset writer Transport.activeTransport.ServerSend(connectionId, channelId, writer.ToArraySegment()); writer.SetLength(0); } // now add to writer in any case // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // // NOTE: it's very possible that we add > MTU to writer if // message size is > MTU. // which is fine. next iteration will just flush it. writer.WriteBytes(segment.Array, segment.Offset, segment.Count); // return queued message to pool NetworkWriterPool.Recycle(message); } // done iterating queued messages. // batch might still contain the last message. // send it. if (writer.Position > 0) { Transport.activeTransport.ServerSend(connectionId, channelId, writer.ToArraySegment()); writer.SetLength(0); } } // reset send time for this channel's batch batch.lastSendTime = NetworkTime.time; }
// add a message for batching // we allow any sized messages. // caller needs to make sure they are within max packet size. public void AddMessage(ArraySegment <byte> message) { // put into a (pooled) writer // -> WriteBytes instead of WriteSegment because the latter // would add a size header. we want to write directly. // -> will be returned to pool when making the batch! // IMPORTANT: NOT adding a size header / msg saves LOTS of bandwidth PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(message.Array, message.Offset, message.Count); messages.Enqueue(writer); }
// Send stage two: serialized NetworkMessage as ArraySegment<byte> internal override void Send(ArraySegment <byte> segment, int channelId = Channels.Reliable) { // get a writer to copy the message into since the segment is only // valid until returning. // => pooled writer will be returned to pool when dequeuing. // => WriteBytes instead of WriteArraySegment because the latter // includes a 4 bytes header. we just want to write raw. //Debug.Log($"Enqueue {BitConverter.ToString(segment.Array, segment.Offset, segment.Count)}"); PooledNetworkWriter writer = NetworkWriterPool.GetWriter(); writer.WriteBytes(segment.Array, segment.Offset, segment.Count); connectionToServer.queue.Enqueue(writer); }