public async Task BeforeSetContinuationTest(int subscribers)
        {
            var condition = new AsyncConditionSource(isSet: true);

            var hits = 0;
            var parallelLoopResult = Parallel.For(0, subscribers, i =>
            {
                condition.GetAwaiter().OnCompleted(() => Interlocked.Increment(ref hits));
            });

            while (parallelLoopResult.IsCompleted == false)
            {
                await Task.Delay(10).ConfigureAwait(false);
            }

            var sw = Stopwatch.StartNew();

            while (sw.ElapsedMilliseconds < 1000 && subscribers != hits)
            {
                Thread.Sleep(10);
            }

            this.logger.Debug($"[TEST] subscribers: {subscribers}, hits: {hits}.");

            Assert.Equal(subscribers, hits);
        }
        public WebSocketClient([NotNull] WebSocketListenerOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (options.Standards.Count == 0)
            {
                throw new ArgumentException("Empty list of WebSocket standards.", nameof(options));
            }

            options.CheckCoherence();
            this.options = options.Clone();
            this.options.SetUsed(true);

            if (this.options.NegotiationTimeout > TimeSpan.Zero)
            {
                this.negotiationsTimeoutQueue = new CancellationQueue(this.options.NegotiationTimeout);
            }
            if (this.options.PingMode != PingMode.Manual)
            {
                this.pingQueue = new PingQueue(options.PingInterval);
            }

            this.log        = this.options.Logger;
            this.closeEvent = new AsyncConditionSource(isSet: true)
            {
                ContinueOnCapturedContext = false
            };
            this.workCancellationSource = new CancellationTokenSource();
            this.pendingRequests        = new ConcurrentDictionary <WebSocketHandshake, Task <WebSocket> >();

            if (this.options.BufferManager == null)
            {
                this.options.BufferManager = BufferManager.CreateBufferManager(100, this.options.SendBufferSize * 2); // create small buffer pool if not configured
            }
            if (this.options.CertificateValidationHandler == null)
            {
                this.options.CertificateValidationHandler = this.ValidateRemoteCertificate;
            }
        }
        /// <inheritdoc />
        internal override async Task <NetworkConnection> ConnectAsync(Uri address, WebSocketListenerOptions options, CancellationToken cancellation)
        {
            if (address == null)
            {
                throw new ArgumentNullException(nameof(address));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var remoteEndPoint = this.GetRemoteEndPoint(address);
            var protocolType   = this.GetProtocolType(address, remoteEndPoint);
            // prepare socket
            var socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Stream, protocolType);

            this.SetupClientSocket(socket, remoteEndPoint);
            try
            {
                // prepare connection
                var socketConnectedCondition = new AsyncConditionSource
                {
                    ContinueOnCapturedContext = false
                };
                var socketAsyncEventArgs = new SocketAsyncEventArgs
                {
                    RemoteEndPoint = remoteEndPoint,
                    UserToken      = socketConnectedCondition
                };

                // connect
                socketAsyncEventArgs.Completed += (_, e) => ((AsyncConditionSource)e.UserToken).Set();

                // interrupt connection when cancellation token is set
                var connectInterruptRegistration = cancellation.CanBeCanceled ?
                                                   cancellation.Register(s => ((AsyncConditionSource)s).Interrupt(new OperationCanceledException()), socketConnectedCondition) :
                                                   default(CancellationTokenRegistration);
                using (connectInterruptRegistration)
                {
                    if (socket.ConnectAsync(socketAsyncEventArgs) == false)
                    {
                        socketConnectedCondition.Set();
                    }

                    await socketConnectedCondition;
                }
                cancellation.ThrowIfCancellationRequested();

                // check connection result
                if (socketAsyncEventArgs.ConnectByNameError != null)
                {
                    throw socketAsyncEventArgs.ConnectByNameError;
                }

                if (socketAsyncEventArgs.SocketError != SocketError.Success)
                {
                    throw new WebSocketException($"Failed to open socket to '{address}' due error '{socketAsyncEventArgs.SocketError}'.",
                                                 new SocketException((int)socketAsyncEventArgs.SocketError));
                }

                var localEndPoint = default(EndPoint);
                try
                {
                    localEndPoint = socket.LocalEndPoint;
                }
                catch
                {
                    if (UnixSocketTransport.IsUnixEndPoint(remoteEndPoint))
                    {
                        localEndPoint = remoteEndPoint;
                    }
                }

                var connection = new SocketConnection(socket, localEndPoint);
                socket = null;
                return(connection);
            }
            finally
            {
                if (socket != null)
                {
                    SafeEnd.Dispose(socket, options.Logger);
                }
            }
        }
Пример #4
0
        public async Task StartAsync()
        {
            if (this.options.Standards.Count <= 0)
            {
                throw new WebSocketException($"There are no WebSocket standards. Please, register standards using {nameof(WebSocketListenerOptions)}.{nameof(WebSocketListenerOptions.Standards)}.");
            }
            if (this.options.Transports.Count <= 0)
            {
                throw new WebSocketException($"There are no WebSocket transports. Please, register transports using {nameof(WebSocketListenerOptions)}.{nameof(WebSocketListenerOptions.Transports)}.");
            }

            if (Interlocked.CompareExchange(ref state, STATE_STARTING, STATE_STOPPED) != STATE_STOPPED)
            {
                throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed or already started.");
            }

            this.options.SetUsed(true);
            var listeners = default(Listener[]);

            try
            {
                if (this.log.IsDebugEnabled)
                {
                    this.log.Debug($"{nameof(WebSocketListener)} is starting.");
                }

                var endPoints = new Tuple <Uri, WebSocketTransport> [this.listenEndPoints.Length];
                for (var i = 0; i < this.listenEndPoints.Length; i++)
                {
                    var listenEndPoint = this.listenEndPoints[i];
                    var transport      = default(WebSocketTransport);
                    if (this.options.Transports.TryGetWebSocketTransport(listenEndPoint, out transport) == false)
                    {
                        throw new WebSocketException($"Unable to find transport for '{listenEndPoint}'. Available transports are: {string.Join(", ", this.options.Transports.SelectMany(t => t.Schemes).Distinct())}.");
                    }

                    endPoints[i] = Tuple.Create(listenEndPoint, transport);
                }

                listeners = new Listener[endPoints.Length];
                for (var i = 0; i < endPoints.Length; i++)
                {
                    listeners[i] = await endPoints[i].Item2.ListenAsync(endPoints[i].Item1, this.options).ConfigureAwait(false);
                }


                this.listeners           = listeners;
                this.localEndPoints      = this.listeners.SelectMany(l => l.LocalEndpoints).ToArray();
                this.stopConditionSource = new AsyncConditionSource(isSet: true)
                {
                    ContinueOnCapturedContext = false
                };

                if (Interlocked.CompareExchange(ref state, STATE_STARTED, STATE_STARTING) != STATE_STARTING)
                {
                    throw new WebSocketException("Failed to start listener from current state. Maybe it is disposed.");
                }

                this.AcceptConnectionsAsync().LogFault(this.log);

                if (this.log.IsDebugEnabled)
                {
                    this.log.Debug($"{nameof(WebSocketListener)} is started.");
                }

                listeners = null;
            }
            catch
            {
                this.options.SetUsed(false);
                throw;
            }
            finally
            {
                // try to revert from starting state to stopped state
                Interlocked.CompareExchange(ref state, STATE_STOPPED, STATE_STARTING);

                if (listeners != null)
                {
                    foreach (var listener in listeners)
                    {
                        SafeEnd.Dispose(listener);
                    }

                    this.listeners           = EmptyListeners;
                    this.localEndPoints      = EmptyEndPoints;
                    this.stopConditionSource = null;
                }
            }
        }