Ejemplo n.º 1
0
 public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
 {
     this.socket               = socket;
     this.remoteEndpoint       = remoteEndPoint;
     this.reusableSendEndPoint = reusableSendEndPoint;
     SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
 }
Ejemplo n.º 2
0
 public KcpServerNonAlloc(Action <int> OnConnected,
                          Action <int, ArraySegment <byte>,
                                  KcpChannel> OnData,
                          Action <int> OnDisconnected,
                          bool DualMode,
                          bool NoDelay,
                          uint Interval,
                          int FastResend         = 0,
                          bool CongestionWindow  = true,
                          uint SendWindowSize    = Kcp.WND_SND,
                          uint ReceiveWindowSize = Kcp.WND_RCV,
                          int Timeout            = KcpConnection.DEFAULT_TIMEOUT,
                          uint MaxRetransmits    = Kcp.DEADLINK,
                          bool MaximizeSendReceiveBuffersToOSLimit = false)
     : base(OnConnected,
            OnData,
            OnDisconnected,
            DualMode,
            NoDelay,
            Interval,
            FastResend,
            CongestionWindow,
            SendWindowSize,
            ReceiveWindowSize,
            Timeout,
            MaxRetransmits,
            MaximizeSendReceiveBuffersToOSLimit)
 {
     // create reusableClientEP either IPv4 or IPv6
     reusableClientEP = DualMode
         ? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0)
         : new IPEndPointNonAlloc(IPAddress.Any, 0);
 }
Ejemplo n.º 3
0
 protected override void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
 {
     // create reusableEP with same address family as remoteEndPoint.
     // otherwise ReceiveFrom_NonAlloc couldn't use it.
     reusableEP = new IPEndPointNonAlloc(addresses[0], port);
     base.CreateRemoteEndPoint(addresses, port);
 }
 public KcpServerNonAlloc(Action <int> OnConnected, Action <int, ArraySegment <byte> > OnData, Action <int> OnDisconnected, bool DualMode, bool NoDelay, uint Interval, int FastResend = 0, bool CongestionWindow = true, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV, int Timeout = KcpConnection.DEFAULT_TIMEOUT)
     : base(OnConnected, OnData, OnDisconnected, DualMode, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout)
 {
     // create reusableClientEP either IPv4 or IPv6
     reusableClientEP = DualMode
         ? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0)
         : new IPEndPointNonAlloc(IPAddress.Any, 0);
 }
Ejemplo n.º 5
0
        protected override KcpServerConnection CreateConnection()
        {
            // IPEndPointNonAlloc is reused all the time.
            // we can't store that as the connection's endpoint.
            // we need a new copy!
            IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint();

            // for allocation free sending, we also need another
            // IPEndPointNonAlloc...
            IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port);

            // create a new KcpConnection NonAlloc version
            // -> where-allocation IPEndPointNonAlloc is reused.
            //    need to create a new one from the temp address.
            return(new KcpServerConnectionNonAlloc(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmits));
        }
        public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
        {
            Log.Info($"KcpClient: connect to {host}:{port}");
            IPAddress[] ipAddress = Dns.GetHostAddresses(host);
            if (ipAddress.Length < 1)
            {
                throw new SocketException((int)SocketError.HostNotFound);
            }

            remoteEndpoint = new IPEndPoint(ipAddress[0], port);
            socket         = new Socket(remoteEndpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            // create reusableEP with same address family as remoteEndPoint.
            // otherwise ReceiveFrom_NonAlloc couldn't use it.
            reusableEP = new IPEndPointNonAlloc(ipAddress[0], port);

            socket.Connect(remoteEndpoint);
            SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);

            // client should send handshake to server as very first message
            SendHandshake();

            RawReceive();
        }
Ejemplo n.º 7
0
        public void TickIncoming()
        {
            while (socket != null && socket.Poll(0, SelectMode.SelectRead))
            {
                try
                {
                    // NOTE: ReceiveFrom allocates.
                    //   we pass our IPEndPoint to ReceiveFrom.
                    //   receive from calls newClientEP.Create(socketAddr).
                    //   IPEndPoint.Create always returns a new IPEndPoint.
                    //   https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
                    //int msgLength = socket.ReceiveFrom(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, ref newClientEP);
                    //Log.Info($"KCP: server raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}");

                    // where-allocation nonalloc ReceiveFrom.
                    int           msgLength     = socket.ReceiveFrom_NonAlloc(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, reusableClientEP);
                    SocketAddress remoteAddress = reusableClientEP.temp;

                    // calculate connectionId from endpoint
                    // NOTE: IPEndPoint.GetHashCode() allocates.
                    //  it calls m_Address.GetHashCode().
                    //  m_Address is an IPAddress.
                    //  GetHashCode() allocates for IPv6:
                    //  https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699
                    //
                    // => using only newClientEP.Port wouldn't work, because
                    //    different connections can have the same port.
                    //int connectionId = newClientEP.GetHashCode();

                    // where-allocation nonalloc GetHashCode
                    int connectionId = remoteAddress.GetHashCode();

                    // IMPORTANT: detect if buffer was too small for the received
                    //            msgLength. otherwise the excess data would be
                    //            silently lost.
                    //            (see ReceiveFrom documentation)
                    if (msgLength <= rawReceiveBuffer.Length)
                    {
                        // is this a new connection?
                        if (!connections.TryGetValue(connectionId, out KcpServerConnection connection))
                        {
                            // IPEndPointNonAlloc is reused all the time.
                            // we can't store that as the connection's endpoint.
                            // we need a new copy!
                            IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint();

                            // for allocation free sending, we also need another
                            // IPEndPointNonAlloc...
                            IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port);

                            // create a new KcpConnection
                            // -> where-allocation IPEndPointNonAlloc is reused.
                            //    need to create a new one from the temp address.
                            connection = new KcpServerConnection(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);

                            // DO NOT add to connections yet. only if the first message
                            // is actually the kcp handshake. otherwise it's either:
                            // * random data from the internet
                            // * or from a client connection that we just disconnected
                            //   but that hasn't realized it yet, still sending data
                            //   from last session that we should absolutely ignore.
                            //
                            //
                            // TODO this allocates a new KcpConnection for each new
                            // internet connection. not ideal, but C# UDP Receive
                            // already allocated anyway.
                            //
                            // expecting a MAGIC byte[] would work, but sending the raw
                            // UDP message without kcp's reliability will have low
                            // probability of being received.
                            //
                            // for now, this is fine.

                            // setup authenticated event that also adds to connections
                            connection.OnAuthenticated = () =>
                            {
                                // only send handshake to client AFTER we received his
                                // handshake in OnAuthenticated.
                                // we don't want to reply to random internet messages
                                // with handshakes each time.
                                connection.SendHandshake();

                                // add to connections dict after being authenticated.
                                connections.Add(connectionId, connection);
                                Log.Info($"KCP: server added connection({connectionId}): {newClientEP}");

                                // setup Data + Disconnected events only AFTER the
                                // handshake. we don't want to fire OnServerDisconnected
                                // every time we receive invalid random data from the
                                // internet.

                                // setup data event
                                connection.OnData = (message) =>
                                {
                                    // call mirror event
                                    //Log.Info($"KCP: OnServerDataReceived({connectionId}, {BitConverter.ToString(message.Array, message.Offset, message.Count)})");
                                    OnData.Invoke(connectionId, message);
                                };

                                // setup disconnected event
                                connection.OnDisconnected = () =>
                                {
                                    // flag for removal
                                    // (can't remove directly because connection is updated
                                    //  and event is called while iterating all connections)
                                    connectionsToRemove.Add(connectionId);

                                    // call mirror event
                                    Log.Info($"KCP: OnServerDisconnected({connectionId})");
                                    OnDisconnected.Invoke(connectionId);
                                };

                                // finally, call mirror OnConnected event
                                Log.Info($"KCP: OnServerConnected({connectionId})");
                                OnConnected.Invoke(connectionId);
                            };

                            // now input the message & process received ones
                            // connected event was set up.
                            // tick will process the first message and adds the
                            // connection if it was the handshake.
                            connection.RawInput(rawReceiveBuffer, msgLength);
                            connection.TickIncoming();

                            // again, do not add to connections.
                            // if the first message wasn't the kcp handshake then
                            // connection will simply be garbage collected.
                        }
                        // existing connection: simply input the message into kcp
                        else
                        {
                            connection.RawInput(rawReceiveBuffer, msgLength);
                        }
                    }
                    else
                    {
                        Log.Error($"KCP Server: message of size {msgLength} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting connectionId={connectionId}.");
                        Disconnect(connectionId);
                    }
                }
                // this is fine, the socket might have been closed in the other end
                catch (SocketException) {}
            }

            // process inputs for all server connections
            // (even if we didn't receive anything. need to tick ping etc.)
            foreach (KcpServerConnection connection in connections.Values)
            {
                connection.TickIncoming();
            }

            // remove disconnected connections
            // (can't do it in connection.OnDisconnected because Tick is called
            //  while iterating connections)
            foreach (int connectionId in connectionsToRemove)
            {
                connections.Remove(connectionId);
            }
            connectionsToRemove.Clear();
        }
Ejemplo n.º 8
0
 public KcpServerConnectionNonAlloc(Socket socket, EndPoint remoteEndpoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT, uint maxRetransmits = Kcp.DEADLINK)
     : base(socket, remoteEndpoint, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout, maxRetransmits)
 {
     this.reusableSendEndPoint = reusableSendEndPoint;
 }