/// <summary> /// Triggers after completion of asynchronous datastream reading has completed transmitting data /// </summary> /// <param name="Result">Represents the status of an asynchronous operation</param> private void ReadPacket(IAsyncResult Result) { //Read in the size of the payload data, making sure the connection is still opened int PacketSize = -1; try { PacketSize = DataStream.EndRead(Result); } //Print an error message, flag the client as dead and exit the function if any exception occurs catch (IOException Exception) { MessageLog.Error(Exception, "Error reading packet size, connection no longer open."); ConnectionDead = true; return; } //Copy the data buffer over into a new array and reset the buffer array for reading in the next packet byte[] PacketBuffer = new byte[PacketSize]; Array.Copy(DataBuffer, PacketBuffer, PacketSize); DataBuffer = new byte[Connection.Available]; //Immediately start reading packets again from the client back into the data buffer, making sure the connection is still open try { DataStream.BeginRead(DataBuffer, 0, DataBuffer.Length, ReadPacket, null); } //Print an error, flag the client as dead and exit the function if any exception occurs catch (IOException Exception) { MessageLog.Error(Exception, "Error registering packet reader function, client connection no longer open."); ConnectionDead = true; return; } //If the connection is new then complete the handshake and upgrade the connection to websockets if (!ConnectionUpgraded) { UpgradeConnection(PacketBuffer); } //Otherwise we need to extract the packet data from the buffer and decode it so we can read and process the messages within else if (PacketSize > 0) { //Visit https://tools.ietf.org/html/rfc6455#section-5.2 for more information on how this decoding works //Lets first extract the data from the first byte byte FirstByte = PacketBuffer[0]; bool FIN = DataExtractor.ReadBit(FirstByte, 0); //Value of 1 indicates if this is the final fragment of the message, this first fragment MAY also be the final fragment bool RSV1 = DataExtractor.ReadBit(FirstByte, 1); //Set to 0 unless an extension is negotatied that defines meanings for non-zero values. Unexpected non-zero values means we should close down the connection. bool RSV2 = DataExtractor.ReadBit(FirstByte, 2); bool RSV3 = DataExtractor.ReadBit(FirstByte, 3); bool[] OpCode = DataExtractor.ReadBits(FirstByte, 4, 7); //Extracting the second byte from the packet buffer byte SecondByte = PacketBuffer[1]; bool MASK = DataExtractor.ReadBit(SecondByte, 0); //Before we go any further we need to figure out the size of the payload data, as this may effect where we read the rest of the data from //Converting the 2nd byte to a binary string, then converting bits 1-7 to decimal gives us the first possible length value of the payload data string SecondByteBinary = BinaryConverter.ByteToBinaryString(PacketBuffer[1]); string PayloadBinary = SecondByteBinary.Substring(1, 7); int PayloadLength = BinaryConverter.BinaryStringToDecimal(PayloadBinary); //Byte indices where we will begin reading in the decoding mask and payload data later on, these will be updated if we needed to read extra bytes to find out the payload length int DecodingMaskIndex = 2; int PayloadDataIndex = 6; //PayloadLength between 0-125 represents the actual length value //With a length equal to 126, we read bytes 2-3 to find the length value if (PayloadLength == 126) { //Bytes 2 and 3 interpreted as 16bit unsigned integer give the PayloadLength byte[] LengthBytes = DataExtractor.ReadBytes(PacketBuffer, 2, 3); PayloadBinary = BinaryConverter.ByteArrayToBinaryString(LengthBytes); PayloadLength = BinaryConverter.BinaryStringToDecimal(PayloadBinary); //Increment the DecodingMask and PayloadData indices by 2, as 3,4 contained the payload length DecodingMaskIndex += 2; PayloadDataIndex += 2; } //Write a length equal to 127, we read bytes 2-9 to find the length value else if (PayloadLength == 127) { //Bytes 2-9 interpreted as a 64bit unsigned integer give the PayloadLength byte[] LengthBytes = DataExtractor.ReadBytes(PacketBuffer, 2, 9); PayloadBinary = BinaryConverter.ByteArrayToBinaryString(LengthBytes); PayloadLength = BinaryConverter.BinaryStringToDecimal(PayloadBinary); DecodingMaskIndex += 8; PayloadDataIndex += 8; } //Extract the decoding mask bytes from the packet buffer byte[] DecodingMask = new byte[4] { PacketBuffer[DecodingMaskIndex], PacketBuffer[DecodingMaskIndex + 1], PacketBuffer[DecodingMaskIndex + 2], PacketBuffer[DecodingMaskIndex + 3] }; //Extract the payload data from the packet buffer, using the mask to decode each byte as we extract it from the packet buffer byte[] PayloadData = new byte[PayloadLength]; for (int i = 0; i < PayloadLength; i++) { PayloadData[i] = (byte)(PacketBuffer[PayloadDataIndex + i] ^ DecodingMask[i % 4]); } //Convert the PayloadData array into an ASCII string string FinalMessage = Encoding.ASCII.GetString(PayloadData); //If the FinalMessage value comes through as "\u0003?" then the connection has been closed from the client side, so we need to set //them as dead so they get cleaned up by the simulation if (FinalMessage == "\u0003?") { ConnectionDead = true; } //Otherwise we just pass the message onto the packet handler as normal so it can be processed accordingly else { PacketHandler.ReadClientPacket(ClientID, FinalMessage); } } }