/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { // pack message and send byte[] message = MessagePacker.Pack(msg); NetworkDiagnostics.OnSend(msg, channelId, message.Length, 1); return(SendBytes(message, channelId)); }
private static NetworkMessageDelegate MessageHandler <T>(Action <INetworkConnection, T> handler) where T : IMessageBase, new() { void AdapterFunction(INetworkConnection conn, NetworkReader reader, int channelId) { // protect against DOS attacks if attackers try to send invalid // data packets to crash the server/client. there are a thousand // ways to cause an exception in data handling: // - invalid headers // - invalid message ids // - invalid data causing exceptions // - negative ReadBytesAndSize prefixes // - invalid utf8 strings // - etc. // // let's catch them all and then disconnect that connection to avoid // further attacks. T message = default(T) != null ? default : new T(); try { message.Deserialize(reader); } finally { NetworkDiagnostics.OnReceive(message, channelId, reader.Length); } handler(conn, message); } return(AdapterFunction); }
public static void Send <T>(IEnumerable <INetworkConnection> connections, T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message into byte[] once MessagePacker.Pack(msg, writer); var segment = writer.ToArraySegment(); int count = 0; foreach (INetworkConnection conn in connections) { if (conn is NetworkConnection networkConnection) { // send to all connections, but don't wait for them _ = networkConnection.SendAsync(segment, channelId); } else { _ = conn.SendAsync(msg, channelId); } count++; } NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count); } }
/// <summary> /// Sends a message, but notify when it is delivered or lost /// </summary> /// <typeparam name="T">type of message to send</typeparam> /// <param name="msg">message to send</param> /// <param name="token">a arbitrary object that the sender will receive with their notification</param> public void SendNotify <T>(T msg, object token, byte channelId = (byte)Channels.Unreliable) { if (sendWindow.Count == WINDOW_SIZE) { NotifyLost?.Invoke(this, token); return; } using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { var notifyPacket = new NotifyPacket { Sequence = (ushort)sequencer.Next(), ReceiveSequence = receiveSequence, AckMask = receiveMask }; sendWindow.Enqueue(new PacketEnvelope { Sequence = notifyPacket.Sequence, Token = token }); MessagePacker.Pack(notifyPacket, writer); MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Length, 1); SendAsync(writer.ToArraySegment(), channelId).Forget(); } }
internal static NetworkMessageDelegate MessageHandler <T>(Action <NetworkConnection, T> handler) where T : IMessageBase, new() => networkMessage => { // protect against DOS attacks if attackers try to send invalid // data packets to crash the server/client. there are a thousand // ways to cause an exception in data handling: // - invalid headers // - invalid message ids // - invalid data causing exceptions // - negative ReadBytesAndSize prefixes // - invalid utf8 strings // - etc. // // let's catch them all and then disconnect that connection to avoid // further attacks. T message = default; try { message = networkMessage.ReadMessage <T>(); } catch (Exception exception) { Debug.LogError("Closed connection: " + networkMessage.conn.connectionId + ". This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: " + exception); networkMessage.conn.Disconnect(); return; } finally { // TODO: Figure out the correct channel NetworkDiagnostics.OnReceive(message, -1, networkMessage.reader.Length); } handler(networkMessage.conn, message); };
internal static NetworkMessageDelegate WrapHandler <T, C>(Action <C, T> handler, bool requireAuthentication) where T : struct, NetworkMessage where C : NetworkConnection => (conn, reader, channelId) => { // protect against DOS attacks if attackers try to send invalid // data packets to crash the server/client. there are a thousand // ways to cause an exception in data handling: // - invalid headers // - invalid message ids // - invalid data causing exceptions // - negative ReadBytesAndSize prefixes // - invalid utf8 strings // - etc. // // let's catch them all and then disconnect that connection to avoid // further attacks. T message = default; try { if (requireAuthentication && !conn.isAuthenticated) { // message requires authentication, but the connection was not authenticated Debug.LogWarning($"Closing connection: {conn}. Received message {typeof(T)} that required authentication, but the user has not authenticated yet"); conn.Disconnect(); return; } //Debug.Log($"ConnectionRecv {conn} msgType:{typeof(T)} content:{BitConverter.ToString(reader.buffer.Array, reader.buffer.Offset, reader.buffer.Count)}"); // if it is a value type, just use default(T) // otherwise allocate a new instance message = reader.Read <T>(); } catch (Exception exception) { Debug.LogError($"Closed connection: {conn}. This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: {exception}"); conn.Disconnect(); return; } finally { // TODO: Figure out the correct channel NetworkDiagnostics.OnReceive(message, channelId, reader.Length); } // user handler exception should not stop the whole server try { // user implemented handler handler((C)conn, message); } catch (Exception e) { Debug.LogError($"Exception in MessageHandler: {e.GetType().Name} {e.Message}\n{e.StackTrace}"); conn.Disconnect(); } };
/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); return(Send(writer.ToArraySegment(), channelId)); } }
/// <summary> /// This sends a network message to the connection. You can await it to check for errors /// </summary> /// <typeparam name="T">The message type</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual UniTask SendAsync <T>(T msg, byte channelId = (byte)Channels.Reliable) { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Length, 1); return(SendAsync(writer.ToArraySegment(), channelId)); } }
/// <summary>Send a NetworkMessage to this connection over the given channel.</summary> public void Send <T>(T msg, int channelId = Channels.Reliable) where T : struct, NetworkMessage { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message and send allocation free MessagePacking.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); Send(writer.ToArraySegment(), channelId); } }
/// <summary> /// This sends a network message with a message ID on the connection. This message is sent on channel zero, which by default is the reliable channel. /// </summary> /// <typeparam name="T">The message type to unregister.</typeparam> /// <param name="msg">The message to send.</param> /// <param name="channelId">The transport layer channel to send on.</param> /// <returns></returns> public virtual bool Send <T>(T msg, int channelId = Channels.DefaultReliable) where T : IMessageBase { NetworkWriter writer = NetworkWriterPool.GetWriter(); // pack message and send allocation free MessagePacker.Pack(msg, writer); NetworkDiagnostics.OnSend(msg, channelId, writer.Position, 1); bool result = Send(writer.ToArraySegment(), channelId); NetworkWriterPool.Recycle(writer); return(result); }
internal static NetworkMessageDelegate MessageHandler <T, C>(Action <C, T> handler, bool requireAuthenication) where T : IMessageBase, new() where C : NetworkConnection => (conn, reader, channelId) => { // protect against DOS attacks if attackers try to send invalid // data packets to crash the server/client. there are a thousand // ways to cause an exception in data handling: // - invalid headers // - invalid message ids // - invalid data causing exceptions // - negative ReadBytesAndSize prefixes // - invalid utf8 strings // - etc. // // let's catch them all and then disconnect that connection to avoid // further attacks. T message = default; try { if (requireAuthenication && !conn.isAuthenticated) { // message requires authentication, but the connection was not authenticated Debug.LogWarning($"Closing connection: {conn}. Received message {typeof(T)} that required authentication, but the user has not authenticated yet"); conn.Disconnect(); return; } // if it is a value type, just use defult(T) // otherwise allocate a new instance message = default(T) != null ? default(T) : new T(); message.Deserialize(reader); } catch (Exception exception) { Debug.LogError("Closed connection: " + conn + ". This can happen if the other side accidentally (or an attacker intentionally) sent invalid data. Reason: " + exception); conn.Disconnect(); return; } finally { // TODO: Figure out the correct channel NetworkDiagnostics.OnReceive(message, channelId, reader.Length); } handler((C)conn, message); };
private static NetworkMessageDelegate MessageHandler <T, C>(Action <C, T> handler, bool requireAuthenication) where T : IMessageBase, new() where C : NetworkConnection { void AdapterFunction(NetworkConnection conn, NetworkReader reader, int channelId) { // protect against DOS attacks if attackers try to send invalid // data packets to crash the server/client. there are a thousand // ways to cause an exception in data handling: // - invalid headers // - invalid message ids // - invalid data causing exceptions // - negative ReadBytesAndSize prefixes // - invalid utf8 strings // - etc. // // let's catch them all and then disconnect that connection to avoid // further attacks. T message = default; try { if (requireAuthenication && !conn.isAuthenticated) { // message requires authentication, but the connection was not authenticated Debug.LogWarning($"Closing connection: {conn}. Received message {typeof(T)} that required authentication, but the user has not authenticated yet"); conn.Disconnect(); return; } message = default(T) != null ? default(T) : new T(); message.Deserialize(reader); } finally { NetworkDiagnostics.OnReceive(message, channelId, reader.Length); } handler((C)conn, message); } return(AdapterFunction); }
public static void Send <T>(IEnumerable <INetworkConnection> connections, T msg, byte channelId = (byte)Channels.Reliable) { using (PooledNetworkWriter writer = NetworkWriterPool.GetWriter()) { // pack message into byte[] once MessagePacker.Pack(msg, writer); var segment = writer.ToArraySegment(); int count = 0; foreach (INetworkConnection conn in connections) { // send to all connections, but don't wait for them conn.SendAsync(segment, channelId).Forget(); count++; } NetworkDiagnostics.OnSend(msg, channelId, segment.Count, count); } }