/// <summary>
        /// Receive a single frame from <paramref name="socket"/>, asynchronously.
        /// </summary>
        /// <param name="socket">The socket to receive from.</param>
        /// <param name="cancellationToken">The token used to propagate notification that this operation should be canceled.</param>
        /// <returns>The content of the received message frame and boolean indicate if another frame of the same message follows.</returns>
        public static Task <(byte[], bool)> ReceiveFrameBytesAsync(
            this NetMQSocket socket,
            CancellationToken cancellationToken = default(CancellationToken)
            )
        {
            if (NetMQRuntime.Current == null)
            {
                throw new InvalidOperationException("NetMQRuntime must be created before calling async functions");
            }

            socket.AttachToRuntime();

            var msg = new Msg();

            msg.InitEmpty();

            if (socket.TryReceive(ref msg, TimeSpan.Zero))
            {
                var  data = msg.CloneData();
                bool more = msg.HasMore;
                msg.Close();

                return(Task.FromResult((data, more)));
            }

            TaskCompletionSource <(byte[], bool)> source = new TaskCompletionSource <(byte[], bool)>();

            CancellationTokenRegistration?registration = null;

            if (cancellationToken.CanBeCanceled)
            {
                registration = cancellationToken.Register(PropagateCancel);
            }

            void Listener(object sender, NetMQSocketEventArgs args)
            {
                if (socket.TryReceive(ref msg, TimeSpan.Zero))
                {
                    var  data = msg.CloneData();
                    bool more = msg.HasMore;
                    msg.Close();

                    socket.ReceiveReady -= Listener;
                    registration?.Dispose();
                    source.TrySetResult((data, more));
                }
            }

            void PropagateCancel()
            {
                socket.ReceiveReady -= Listener;
                registration?.Dispose();
                source.TrySetCanceled();
            }

            socket.ReceiveReady += Listener;

            return(source.Task);
        }
        public static Task <(string, bool)> ReceiveFrameStringAsync(
            [NotNull] this NetMQSocket socket,
            [NotNull] Encoding encoding,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (NetMQRuntime.Current == null)
            {
                throw new InvalidOperationException("NetMQRuntime must be created before calling async functions");
            }

            socket.AttachToRuntime();

            var msg = new Msg();

            msg.InitEmpty();

            if (socket.TryReceive(ref msg, TimeSpan.Zero))
            {
                var str = msg.Size > 0
                    ? encoding.GetString(msg.Data, msg.Offset, msg.Size)
                    : string.Empty;

                msg.Close();
                return(Task.FromResult((str, msg.HasMore)));
            }

            TaskCompletionSource <(string, bool)> source = new TaskCompletionSource <(string, bool)>();

            cancellationToken.Register(() => source.SetCanceled());

            void Listener(object sender, NetMQSocketEventArgs args)
            {
                if (socket.TryReceive(ref msg, TimeSpan.Zero))
                {
                    var str = msg.Size > 0
                        ? encoding.GetString(msg.Data, msg.Offset, msg.Size)
                        : string.Empty;
                    bool more = msg.HasMore;

                    msg.Close();
                    socket.ReceiveReady -= Listener;
                    source.SetResult((str, more));
                }
            }

            socket.ReceiveReady += Listener;

            return(source.Task);
        }
        public static Task <(byte[], bool)> ReceiveFrameBytesAsync([NotNull] this NetMQSocket socket)
        {
            if (NetMQRuntime.Current == null)
            {
                throw new InvalidOperationException("NetMQRuntime must be created before calling async functions");
            }

            socket.AttachToRuntime();

            var msg = new Msg();

            msg.InitEmpty();

            if (socket.TryReceive(ref msg, TimeSpan.Zero))
            {
                var  data = msg.CloneData();
                bool more = msg.HasMore;
                msg.Close();

                return(Task.FromResult((data, more)));
            }

            TaskCompletionSource <(byte[], bool)> source = new TaskCompletionSource <(byte[], bool)>();

            void Listener(object sender, NetMQSocketEventArgs args)
            {
                if (socket.TryReceive(ref msg, TimeSpan.Zero))
                {
                    var  data = msg.CloneData();
                    bool more = msg.HasMore;
                    msg.Close();

                    socket.ReceiveReady -= Listener;
                    source.SetResult((data, more));
                }
            }

            socket.ReceiveReady += Listener;

            return(source.Task);
        }
        /// <summary>
        /// Receive a single frame from <paramref name="socket"/>, asynchronously, then ignore its content.
        /// </summary>
        /// <param name="socket">The socket to receive from.</param>
        /// <returns>Boolean indicate if another frame of the same message follows</returns>
        public static Task <bool> SkipFrameAsync([NotNull] this NetMQSocket socket)
        {
            if (NetMQRuntime.Current == null)
            {
                throw new InvalidOperationException("NetMQRuntime must be created before calling async functions");
            }

            socket.AttachToRuntime();

            var msg = new Msg();

            msg.InitEmpty();

            if (socket.TryReceive(ref msg, TimeSpan.Zero))
            {
                bool more = msg.HasMore;
                msg.Close();

                return(more ? s_trueTask : s_falseTask);
            }

            TaskCompletionSource <bool> source = new TaskCompletionSource <bool>();

            void Listener(object sender, NetMQSocketEventArgs args)
            {
                if (socket.TryReceive(ref msg, TimeSpan.Zero))
                {
                    bool more = msg.HasMore;
                    msg.Close();
                    socket.ReceiveReady -= Listener;
                    source.SetResult(more);
                }
            }

            socket.ReceiveReady += Listener;

            return(source.Task);
        }