private static unsafe void Transmit() { // Transmit anything in buffers while (HasSendDataQueued) { MessageStream *sendStream = MessageStreamManager.PlayerConnectionMt_DequeSendStream(); if (sendStream == null) // rare return value in multithreading situations when the queue is not empty { continue; } MessageStream.MessageStreamBuffer *bufferRead = sendStream->BufferRead; int offset = 0; while (bufferRead != null) { if (bufferRead->Size > 0) { #if UNITY_WEBGL uint actualRead = WebSocketSend(bufferRead->Buffer + offset, bufferRead->Size - offset); if (actualRead == 0xffffffff) #else uint actualRead = Baselib_Socket_TCP_Send(hSocket, bufferRead->Buffer + offset, (uint)(bufferRead->Size - offset), (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState)); if (errState.code != Baselib_ErrorCode.Success) #endif { // Something bad happened; lost connection maybe? // After cleaning up, next time we will try to re-initialize Disconnect(); initRetryCounter = kInitRetryCounter; return; } if (actualRead == 0) { // Move the data to be sent to the front of this buffer for next time sendStream->RecyclePartialStream(bufferRead, offset); MessageStreamManager.PlayerConnectionMt_QueueSendStream((IntPtr)sendStream); return; } offset += (int)actualRead; } if (offset == bufferRead->Size) { bufferRead = bufferRead->Next; offset = 0; } } MessageStreamManager.RecycleStream(sendStream); } }
internal unsafe void BufferTestRecycleInternal(string[] textData, int recycleSize) { MessageStream buf = new MessageStream(16); MessageStream.MessageStreamBuffer *recycleUntil = buf.BufferRead; int recycleOffset = recycleSize; string totalText = ""; int sizeBeforeWriteBuffer = 0; int totalSize = 0; foreach (string text in textData) { totalText += text; bool incRecycleUntil = false; if (text.Length * 2 + buf.BufferWrite->Size > buf.BufferWrite->Capacity) { sizeBeforeWriteBuffer += buf.BufferWrite->Size; if (sizeBeforeWriteBuffer < recycleSize) { incRecycleUntil = true; recycleOffset -= buf.BufferWrite->Size; } } BufferTestWriteInternal(ref buf, text); if (incRecycleUntil) { recycleUntil = recycleUntil->Next; } totalSize += text.Length * 2; } int preRecycleUntilSize = recycleUntil->Size; int preRecycleUntilNextSize = recycleUntil->Next != null ? recycleUntil->Next->Size : -1; int preRecycleWriteSize = buf.BufferWrite->Size; buf.RecyclePartialStream(recycleUntil, recycleOffset); // After recycling---- // - Find first buffer with data remaining after recycling MessageStream.MessageStreamBuffer *firstBuffer = buf.BufferRead; while (firstBuffer->Size == 0 && firstBuffer->Next != null) { firstBuffer = firstBuffer->Next; } // - if recycleOffset == size of the last buffer, next buffer should be "First buffer remaining" and be full // - otherwise, ensure size in first buffer with data is as expected if (preRecycleUntilSize == recycleOffset) { Assert.AreEqual(preRecycleUntilNextSize, firstBuffer->Size); } else { Assert.AreEqual(preRecycleUntilSize - recycleOffset, firstBuffer->Size); } unsafe { Assert.AreEqual(totalText.Substring(recycleSize / 2, firstBuffer->Size / 2), new string((char *)firstBuffer->Buffer, 0, firstBuffer->Size / 2)); } // - Check write buffer - if different than first buffer remaining it should be same as before // - otherwise should be pre-recycle write buffer size minus recycleOffset (which should work out the same as previous check) if (firstBuffer->Buffer == buf.BufferWrite->Buffer) { Assert.AreEqual(preRecycleWriteSize - recycleOffset, buf.BufferWrite->Size); } else { Assert.AreEqual(preRecycleWriteSize, buf.BufferWrite->Size); } unsafe { if (firstBuffer->Buffer == buf.BufferWrite->Buffer) { Assert.AreEqual(totalText.Substring(recycleSize / 2, (totalSize - recycleSize) / 2), new string((char *)buf.BufferWrite->Buffer, 0, buf.BufferWrite->Size / 2)); } else { Assert.AreEqual(totalText.Substring(sizeBeforeWriteBuffer / 2, buf.BufferWrite->Size / 2), new string((char *)buf.BufferWrite->Buffer, 0, buf.BufferWrite->Size / 2)); } } // - Check read buffer - if smaller than first data size it should be 0 // - otherwise if recycled more than its size it should be 0 // - otherwise it should be the first buffer with data remaining // - If recycled including full offset, read should be 0 and not have a next if (buf.BufferRead->Capacity < textData[0].Length * 2) { Assert.AreEqual(0, buf.BufferRead->Size); } else if (buf.BufferRead->Size <= recycleSize) { Assert.AreEqual(0, buf.BufferRead->Size); } else { Assert.AreEqual(buf.BufferRead->Buffer, firstBuffer->Buffer); } if (preRecycleUntilSize == recycleOffset) { Assert.AreEqual(0, buf.BufferRead->Size); Assert.AreEqual(IntPtr.Zero, (IntPtr)buf.BufferRead->Next); } unsafe { Assert.AreEqual(totalText.Substring(recycleSize / 2, buf.BufferRead->Size / 2), new string((char *)buf.BufferRead->Buffer, 0, buf.BufferRead->Size / 2)); } // - Check other details Assert.AreEqual(totalSize - recycleSize, buf.TotalBytes); }
private static unsafe void Receive() { // Receive anything sent to us // Similar setup for sending data MessageHeader *header = (MessageHeader *)receiveStream.BufferRead->Buffer; int bytesNeeded = 0; if (receiveStream.TotalBytes < sizeof(MessageHeader)) { bytesNeeded = sizeof(MessageHeader) - receiveStream.TotalBytes; } else { bytesNeeded = sizeof(MessageHeader) + header->bytes - receiveStream.TotalBytes; } while (bytesNeeded > 0) { MessageStream.MessageStreamBuffer *bufferWrite = receiveStream.BufferWrite; int bytesAvail = bufferWrite->Capacity - bufferWrite->Size; #if UNITY_WEBGL uint actualWritten = WebSocketReceive(bufferWrite->Buffer + bufferWrite->Size, bytesNeeded <= bytesAvail ? bytesNeeded : bytesAvail); if (actualWritten == 0xffffffff) #else uint actualWritten = Baselib_Socket_TCP_Recv(hSocket, bufferWrite->Buffer + bufferWrite->Size, (uint)(bytesNeeded <= bytesAvail ? bytesNeeded : bytesAvail), (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState)); if (errState.code != Baselib_ErrorCode.Success) #endif { // Something bad happened; lost connection maybe? // After cleaning up, next time we will try to re-initialize Disconnect(); initRetryCounter = kInitRetryCounter; return; } if (bytesNeeded > 0 && actualWritten == 0) { // Don't do anything with data until we've received everything return; } receiveStream.UpdateSize((int)actualWritten); bytesNeeded -= (int)actualWritten; if (bytesNeeded == 0) { // Finished receiving header if (receiveStream.TotalBytes == sizeof(MessageHeader)) { // De-synced somewhere... reset connection if (header->magicId != EditorMessageIds.kMagicNumber) { Disconnect(); initRetryCounter = kInitRetryCounter; return; } bytesNeeded = header->bytes; receiveStream.Allocate(bytesNeeded); } // Finished receiving message if (bytesNeeded == 0) { // Otherwise bytesNeeded becomes 0 after message is finished, which can be immediately in the // case of PlayerMessageId.kApplicationQuit (message size 0) foreach (var e in m_EventMessageList) { if (e.messageId == header->messageId) { // This could be anything from a 4-byte "bool" to an asset sent from the editor byte[] messageData = receiveStream.ToByteArray(sizeof(MessageHeader), receiveStream.TotalBytes); e.Invoke(0, messageData); } } if (header->messageId == EditorMessageIds.kApplicationQuit) { UnityEngine.Debug.Log("Unity editor has been closed"); Disconnect(); } if (header->messageId == EditorMessageIds.kNoFurtherConnections) { UnityEngine.Debug.Log("Unity editor can not accept any more connections"); Disconnect(); } // Poll for next message receiveStream.RecycleStreamAndFreeExtra(); bytesNeeded = sizeof(MessageHeader); } } } // This code should not be executable throw new InvalidOperationException("Internal error receiving network data"); }