static void SendMessage(TcpClient client, byte[] payload, int opcode, bool masking, byte[] mask) { if (masking && mask == null) { throw new ArgumentException(nameof(mask)); } using (var packet = new MemoryStream()) { byte firstbyte = 0b0_0_0_0_0000; // fin | rsv1 | rsv2 | rsv3 | [ OPCODE | OPCODE | OPCODE | OPCODE ] firstbyte |= 0b1_0_0_0_0000; // fin //firstbyte |= 0b0_1_0_0_0000; // rsv1 //firstbyte |= 0b0_0_1_0_0000; // rsv2 //firstbyte |= 0b0_0_0_1_0000; // rsv3 firstbyte += (byte)opcode; // Text packet.WriteByte(firstbyte); // Set bit: bytes[byteIndex] |= mask; byte secondbyte = 0b0_0000000; // mask | [SIZE | SIZE | SIZE | SIZE | SIZE | SIZE | SIZE] if (masking) { secondbyte |= 0b1_0000000; // mask } if (payload.LongLength <= 0b0_1111101) // 125 { secondbyte |= (byte)payload.Length; packet.WriteByte(secondbyte); } else if (payload.LongLength <= UInt16.MaxValue) // If length takes 2 bytes { secondbyte |= 0b0_1111110; // 126 packet.WriteByte(secondbyte); var len = BitConverter.GetBytes(payload.LongLength); Array.Reverse(len, 0, 2); packet.Write(len, 0, 2); } else // if (payload.LongLength <= Int64.MaxValue) // If length takes 8 bytes { secondbyte |= 0b0_1111111; // 127 packet.WriteByte(secondbyte); var len = BitConverter.GetBytes(payload.LongLength); Array.Reverse(len, 0, 8); packet.Write(len, 0, 8); } if (masking) { packet.Write(mask, 0, 4); payload = ApplyMask(payload, mask); } // Write all data to the packet packet.Write(payload, 0, payload.Length); // Get client's stream var stream = client.GetStream(); var finalPacket = packet.ToArray(); TcpServer.WriteLogSafe($@"SENT: {BitConverter.ToString(finalPacket)}"); // Send the packet foreach (var b in finalPacket) { stream.WriteByte(b); } } }
public static void Listen(TcpServer server) { server.ClientConnected += (_, client) => OnConnected(client); }
private void MessageLoop() { var session = this; var client = session.Client; var stream = session.ClientStream; var packet = new List <byte>(); var messageOpcode = 0x0; using (var messageBuffer = new MemoryStream()) while (client.Connected) { packet.Clear(); var ab = client.Available; if (ab == 0) { MessageProcessQueue(); continue; } packet.Add((byte)stream.ReadByte()); var fin = (packet[0] & (1 << 7)) != 0; var rsv1 = (packet[0] & (1 << 6)) != 0; var rsv2 = (packet[0] & (1 << 5)) != 0; var rsv3 = (packet[0] & (1 << 4)) != 0; // Must error if is set. //if (rsv1 || rsv2 || rsv3) // return; var opcode = packet[0] & ((1 << 4) - 1); switch (opcode) { case 0x0: // Continuation Frame break; case 0x1: // Text case 0x2: // Binary case 0x8: // Connection Close messageOpcode = opcode; break; case 0x9: continue; // Ping case 0xA: continue; // Pong default: continue; // Reserved } packet.Add((byte)stream.ReadByte()); var masked = IsMasking = (packet[1] & (1 << 7)) != 0; var pseudoLength = packet[1] - (masked ? 128 : 0); ulong actualLength = 0; if (pseudoLength > 0 && pseudoLength < 125) { actualLength = (ulong)pseudoLength; } else if (pseudoLength == 126) { var length = new byte[2]; stream.Read(length, 0, length.Length); packet.AddRange(length); Array.Reverse(length); actualLength = BitConverter.ToUInt16(length, 0); } else if (pseudoLength == 127) { var length = new byte[8]; stream.Read(length, 0, length.Length); packet.AddRange(length); Array.Reverse(length); actualLength = BitConverter.ToUInt64(length, 0); } var mask = new byte[4]; if (masked) { stream.Read(mask, 0, mask.Length); packet.AddRange(mask); } if (actualLength > 0) { var data = new byte[actualLength]; stream.Read(data, 0, data.Length); packet.AddRange(data); if (masked) { data = ApplyMask(data, mask); } messageBuffer.Write(data, 0, data.Length); } TcpServer.WriteLogSafe($@"RECV: {BitConverter.ToString(packet.ToArray())}"); if (!fin) { continue; } var message = messageBuffer.ToArray(); switch (messageOpcode) { case 0x1: AnyMessageReceived?.Invoke(session, message); TextMessageReceived?.Invoke(session, Encoding.UTF8.GetString(message)); break; case 0x2: AnyMessageReceived?.Invoke(session, message); BinaryMessageReceived?.Invoke(session, message); break; case 0x8: Close(); break; default: throw new Exception("Invalid opcode: " + messageOpcode); } messageBuffer.SetLength(0); } }