void ReadCallback(IAsyncResult result)
        {
            Interlocked.Decrement(ref ActiveListeners);
            Interlocked.Increment(ref PacketsReceived);

            var      message = (MessageReader)result.AsyncState;
            int      bytesReceived;
            EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);

            //End the receive operation
            try
            {
                bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint);

                message.Offset = 0;
                message.Length = bytesReceived;
            }
            catch (SocketException)
            {
                // Client no longer reachable, pretend it didn't happen
                // TODO should this not inform the connection this client is lost???

                // This thread suggests the IP is not passed out from WinSoc so maybe not possible
                // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
                message.Recycle();

                StartListeningForData();
                return;
            }
            catch (Exception ex)
            {
                //If the socket's been disposed then we can just end there.
                this.Logger?.Invoke("Stopped due to: " + ex.Message);
                return;
            }

            // I'm a little concerned about a infinite loop here, but it seems like it's possible
            // to get 0 bytes read on UDP without the socket being shut down.
            if (bytesReceived == 0)
            {
                message.Recycle();
                StartListeningForData();
                return;
            }

            //Begin receiving again
            StartListeningForData();

            bool aware        = true;
            bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello;
            bool isHello      = hasHelloByte && message.Length >= MinConnectionLength;

            //If we're aware of this connection use the one already
            //If this is a new client then connect with them!
            UdpServerConnection connection;

            if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
            {
                //Check for malformed connection attempts
                if (!isHello)
                {
                    message.Recycle();
                    return;
                }

                lock (this.allConnections)
                {
                    aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
                    if (!aware)
                    {
                        connection = new UdpServerConnection(this, (IPEndPoint)remoteEndPoint, this.IPMode);
                        if (!this.allConnections.TryAdd(remoteEndPoint, connection))
                        {
                            throw new Exception();
                        }
                    }
                }
            }

            //Inform the connection of the buffer (new connections need to send an ack back to client)
            connection.HandleReceive(message, bytesReceived);

            //If it's a new connection invoke the NewConnection event.
            if (!aware)
            {
                // Skip header and hello byte;
                message.Offset   = 4;
                message.Length   = bytesReceived - 4;
                message.Position = 0;
                InvokeNewConnection(message, connection);
            }
            else if (isHello || (!isHello && hasHelloByte))
            {
                message.Recycle();
            }
        }
Esempio n. 2
0
        /// <summary>
        ///     Called when data has been received by the listener.
        /// </summary>
        /// <param name="result">The asyncronous operation's result.</param>
        void ReadCallback(IAsyncResult result)
        {
            int      bytesReceived;
            EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);

            //End the receive operation
            try
            {
                lock (listener)
                    bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint);
            }
            catch (ObjectDisposedException)
            {
                //If the socket's been disposed then we can just end there.
                return;
            }
            catch (SocketException e)
            {
                //Errrr... shit...
                //Not exactly much we can do if we've got here
                throw e;
            }

            //Exit if no bytes read, we've closed.
            if (bytesReceived == 0)
            {
                return;
            }

            //Copy to new buffer
            byte[] buffer = new byte[bytesReceived];
            Buffer.BlockCopy((byte[])result.AsyncState, 0, buffer, 0, bytesReceived);

            //Begin receiving again
            StartListeningForData();

            bool aware;
            UdpServerConnection connection;

            lock (connections)
            {
                aware = connections.ContainsKey(remoteEndPoint);

                //If we're aware of this connection use the one already
                if (aware)
                {
                    connection = connections[remoteEndPoint];
                }

                //If this is a new client then connect with them!
                else
                {
                    //Check for malformed connection attempts
                    if (buffer[0] != (byte)SendOptionInternal.Hello || buffer.Length != 3)
                    {
                        return;
                    }

                    connection = new UdpServerConnection(this, remoteEndPoint, IPMode);
                    connections.Add(remoteEndPoint, connection);

                    //Then ping back an ack to make sure they're happy (unless we rejected them...)
                    connection.SendAck(buffer[1], buffer[2]);
                }
            }

            //And fire the corresponding event
            if (aware)
            {
                connection.InvokeDataReceived(buffer);
            }
            else
            {
                InvokeNewConnection(connection);
            }
        }
Esempio n. 3
0
        /// <summary>
        ///     Called when data has been received by the listener.
        /// </summary>
        /// <param name="result">The asyncronous operation's result.</param>
        void ReadCallback(IAsyncResult result)
        {
            int      bytesReceived;
            EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);

            //End the receive operation
            try
            {
                lock (listener)
                    bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint);
            }
            catch (ObjectDisposedException)
            {
                //If the socket's been disposed then we can just end there.
                return;
            }
            catch (SocketException)
            {
                //Client no longer reachable, pretend it didn't happen
                //TODO should this not inform the connection this client is lost???

                //This thread suggests the IP is not passed out from WinSoc so maybe not possible
                //http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054

                StartListeningForData();
                return;
            }

            //Exit if no bytes read, we've closed.
            if (bytesReceived == 0)
            {
                return;
            }

            //Copy to new buffer
            byte[] buffer = new byte[bytesReceived];
            Buffer.BlockCopy((byte[])result.AsyncState, 0, buffer, 0, bytesReceived);

            //Begin receiving again
            StartListeningForData();

            bool aware;
            UdpServerConnection connection;

            lock (connections)
            {
                aware = connections.ContainsKey(remoteEndPoint);

                //If we're aware of this connection use the one already
                if (aware)
                {
                    connection = connections[remoteEndPoint];
                }
                //If this is a new client then connect with them!
                else
                {
                    if (ArrayCompare(buffer, 0, MagicSequence, 0, MagicSequence.Length))
                    {
                        InvokeUnconnectedData(remoteEndPoint, buffer);
                        return;
                    }

                    //Check for malformed connection attempts
                    if (buffer[0] != (byte)UdpSendOption.Hello)
                    {
                        return;
                    }

                    connection = new UdpServerConnection(this, remoteEndPoint, IPMode);
                    connections.Add(remoteEndPoint, connection);
                }
            }

            connection.LastMessage = DateTime.Now;

            //Inform the connection of the buffer (new connections need to send an ack back to client)
            connection.HandleReceive(buffer);

            //If it's a new connection invoke the NewConnection event.
            if (!aware)
            {
                byte[] dataBuffer = new byte[buffer.Length - 1];
                Buffer.BlockCopy(buffer, 1, dataBuffer, 0, buffer.Length - 1);
                InvokeNewConnection(dataBuffer, connection);
            }
        }
        void ReadCallback(IAsyncResult result)
        {
            var      message = (MessageReader)result.AsyncState;
            int      bytesReceived;
            EndPoint remoteEndPoint = new IPEndPoint(this.EndPoint.Address, this.EndPoint.Port);

            //End the receive operation
            try
            {
                bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint);

                message.Offset = 0;
                message.Length = bytesReceived;
            }
            catch (ObjectDisposedException)
            {
                message.Recycle();
                return;
            }
            catch (SocketException sx)
            {
                // Client no longer reachable, pretend it didn't happen
                // TODO should this not inform the connection this client is lost???

                // This thread suggests the IP is not passed out from WinSoc so maybe not possible
                // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
                message.Recycle();
                this.Logger?.Invoke($"Socket Ex {sx.SocketErrorCode} in ReadCallback: {sx.Message}");

                Thread.Sleep(10);
                StartListeningForData();
                return;
            }
            catch (Exception ex)
            {
                //If the socket's been disposed then we can just end there.
                message.Recycle();
                this.Logger?.Invoke("Stopped due to: " + ex.Message);
                return;
            }

            // I'm a little concerned about a infinite loop here, but it seems like it's possible
            // to get 0 bytes read on UDP without the socket being shut down.
            if (bytesReceived == 0)
            {
                message.Recycle();
                this.Logger?.Invoke("Received 0 bytes");
                Thread.Sleep(10);
                StartListeningForData();
                return;
            }

            //Begin receiving again
            StartListeningForData();

            bool aware   = true;
            bool isHello = message.Buffer[0] == (byte)UdpSendOption.Hello;

            // If we're aware of this connection use the one already
            // If this is a new client then connect with them!
            UdpServerConnection connection;

            if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
            {
                lock (this.allConnections)
                {
                    if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
                    {
                        // Check for malformed connection attempts
                        if (!isHello)
                        {
                            message.Recycle();
                            return;
                        }

                        if (AcceptConnection != null)
                        {
                            if (!AcceptConnection((IPEndPoint)remoteEndPoint, message.Buffer, out var response))
                            {
                                message.Recycle();
                                if (response != null)
                                {
                                    SendData(response, response.Length, remoteEndPoint);
                                }

                                return;
                            }
                        }

                        aware      = false;
                        connection = new UdpServerConnection(this, (IPEndPoint)remoteEndPoint, this.IPMode);
                        if (!this.allConnections.TryAdd(remoteEndPoint, connection))
                        {
                            throw new HazelException("Failed to add a connection. This should never happen.");
                        }
                    }
                }
            }

            // If it's a new connection invoke the NewConnection event.
            // This needs to happen before handling the message because in localhost scenarios, the ACK and
            // subsequent messages can happen before the NewConnection event sets up OnDataRecieved handlers
            if (!aware)
            {
                // Skip header and hello byte;
                message.Offset   = 4;
                message.Length   = bytesReceived - 4;
                message.Position = 0;
                InvokeNewConnection(message, connection);
            }

            // Inform the connection of the buffer (new connections need to send an ack back to client)
            connection.HandleReceive(message, bytesReceived);

            if (aware && isHello)
            {
                message.Recycle();
            }
        }