123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- #if NETSTANDARD2_0 || NETSTANDARD2_1 || NET47
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- namespace NetMQ
- {
- /// <summary>
- /// Provides extension methods for the <see cref="NetMQSocket"/>,
- /// via which messages may be received asynchronously.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "UnusedMember.Global")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
- public static class AsyncReceiveExtensions
- {
- static Task<bool> s_trueTask = Task.FromResult(true);
- static Task<bool> s_falseTask = Task.FromResult(false);
- #region Receiving frames as a multipart message
- /// <summary>
- /// Receive a single frame from <paramref name="socket"/>, asynchronously.
- /// </summary>
- /// <param name="socket">The socket to receive from.</param>
- /// <param name="expectedFrameCount">Specifies the initial capacity of the <see cref="List{T}"/> used
- /// to buffer results. If the number of frames is known, set it here. If more frames arrive than expected,
- /// an extra allocation will occur, but the result will still be correct.</param>
- /// <param name="cancellationToken">The token used to propagate notification that this operation should be canceled.</param>
- /// <returns>The content of the received message.</returns>
- public static async Task<NetMQMessage> ReceiveMultipartMessageAsync(
- this NetMQSocket socket,
- int expectedFrameCount = 4,
- CancellationToken cancellationToken = default(CancellationToken))
- {
- var message = new NetMQMessage(expectedFrameCount);
- while (true)
- {
- (byte[] bytes, bool more) = await socket.ReceiveFrameBytesAsync(cancellationToken);
- message.Append(bytes);
- if (!more)
- {
- break;
- }
- }
- return message;
- }
- #endregion
- #region Receiving a frame as a byte array
- /// <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;
- }
- #endregion
- #region Receiving a frame as a string
- /// <summary>
- /// Receive a single frame from <paramref name="socket"/>, asynchronously, and decode as a string using <see cref="SendReceiveConstants.DefaultEncoding"/>.
- /// </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 as a string and a boolean indicate if another frame of the same message follows.</returns>
- public static Task<(string, bool)> ReceiveFrameStringAsync(
- this NetMQSocket socket,
- CancellationToken cancellationToken = default(CancellationToken)
- )
- {
- return socket.ReceiveFrameStringAsync(SendReceiveConstants.DefaultEncoding, cancellationToken);
- }
- /// <summary>
- /// Receive a single frame from <paramref name="socket"/>, asynchronously, and decode as a string using <paramref name="encoding"/>.
- /// </summary>
- /// <param name="socket">The socket to receive from.</param>
- /// <param name="encoding">The encoding used to convert the frame's data to a string.</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 as a string and boolean indicate if another frame of the same message follows..</returns>
- public static Task<(string, bool)> ReceiveFrameStringAsync(
- this NetMQSocket socket,
- 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
- ? msg.GetString(encoding)
- : string.Empty;
- msg.Close();
- return Task.FromResult((str, msg.HasMore));
- }
- TaskCompletionSource<(string, bool)> source = new TaskCompletionSource<(string,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 str = msg.Size > 0
- ? msg.GetString(encoding)
- : string.Empty;
- bool more = msg.HasMore;
- msg.Close();
- socket.ReceiveReady -= Listener;
- registration?.Dispose();
- source.TrySetResult((str, more));
- }
- }
- void PropagateCancel()
- {
- socket.ReceiveReady -= Listener;
- registration?.Dispose();
- source.TrySetCanceled();
- }
- socket.ReceiveReady += Listener;
- return source.Task;
- }
- #endregion
- #region Skipping a message
- /// <summary>
- /// Receive a single frame from <paramref name="socket"/>, asynchronously, then ignore its content.
- /// </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>Boolean indicate if another frame of the same message follows</returns>
- public static Task<bool> SkipFrameAsync(
- 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))
- {
- bool more = msg.HasMore;
- msg.Close();
- return more ? s_trueTask : s_falseTask;
- }
- TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
- CancellationTokenRegistration? registration = null;
- if (cancellationToken.CanBeCanceled)
- {
- registration = cancellationToken.Register(PropagateCancel);
- }
- void Listener(object sender, NetMQSocketEventArgs args)
- {
- if (socket.TryReceive(ref msg, TimeSpan.Zero))
- {
- bool more = msg.HasMore;
- msg.Close();
- socket.ReceiveReady -= Listener;
- registration?.Dispose();
- source.TrySetResult(more);
- }
- }
- void PropagateCancel()
- {
- socket.ReceiveReady -= Listener;
- registration?.Dispose();
- source.TrySetCanceled();
- }
- socket.ReceiveReady += Listener;
- return source.Task;
- }
- #endregion
- #region Skipping all frames of a multipart message
- /// <summary>
- /// Receive all frames of the next message from <paramref name="socket"/>, asynchronously, then ignore their contents.
- /// </summary>
- /// <param name="socket">The socket to receive from.</param>
- public static async Task SkipMultipartMessageAsync(this NetMQSocket socket)
- {
- while (true)
- {
- bool more = await socket.SkipFrameAsync();
- if (!more)
- break;
- }
- }
- #endregion
- #region Receiving a routing key
- /// <summary>
- /// Receive a routing-key from <paramref name="socket"/>, blocking until one arrives.
- /// </summary>
- /// <param name="socket">The socket to receive from.</param>
- /// <returns>The routing key and a boolean indicate if another frame of the same message follows.</returns>
- public static async Task<(RoutingKey, bool)> ReceiveRoutingKeyAsync(this NetMQSocket socket)
- {
- var (bytes, more) = await socket.ReceiveFrameBytesAsync();
- return (new RoutingKey(bytes), more);
- }
- #endregion
- }
- }
- #endif
|