private async Task AcceptLoopAsync() { while (!acceptCts.IsCancellationRequested) { try { var socket = await socketListener.AcceptAsync().ConfigureAwait(false); if (acceptCts.IsCancellationRequested) { return; } // use Math.abs to fix overflows if they ever happen. int connectionId = Math.Abs(Interlocked.Increment(ref connectionIdCounter)); var connection = new Connection(connectionId, socket, this); logger?.LogInformation($"Connection accepted {{ connectionId={connectionId} }}."); // We have a socket, setup the message stream var messageStream = new EventMessageStream <TMessage>( deserializer, serializer, new SocketDuplexMessageStream(cancellationToken => new ValueTask <Socket>(socket)), msg => HandleMessagAsync(connection, msg), () => HandleKeepAliveAsync(connection), (ex) => HandleConnectionDisconnectAsync(connection, ex, false), clientMessageStreamOptions, rpcKeyResolver ); connection.State = connectionStateProvider(connection); connection.MessageStream = messageStream; if (!connections.TryAdd(connectionId, connection)) { // TODO handle this. Shouldn't ever happen, but we should just disconnect the client. var dcTask = connection.DisconnectAsync(); logger?.LogWarning($"Could not add connection to connection list. Disconnected connection. {{ connectionId={connectionId} }}."); continue; } try { await messageStream.OpenAsync().ConfigureAwait(false); logger?.LogDebug($"message-stream initialized. {{ connectionId={connectionId} }}"); } catch (Exception ex) { logger?.LogError(ex, $"Error initializing message-stream. {{ connectionId={connectionId} }}."); var dcTask = connection.DisconnectAsync(); connections.TryRemove(connectionId, out var con); continue; } var _ = Task.Run(async() => { await pendingConnectionLock.WaitAsync().ConfigureAwait(false); try { await handleConnectionDelegate(connection).ConfigureAwait(false); logger?.LogDebug($"Connection initialized {{ connectionId={connectionId} }}."); } catch (Exception ex) { logger?.LogError(ex, $"Error initializing connection. Disconnecting. {{ connectionId={connectionId} }}"); var dcTask = connection.DisconnectAsync(); connections.TryRemove(connectionId, out var con); } finally { pendingConnectionLock.Release(); } }); } catch (Exception ex) { logger?.LogError(ex, "Exception in accept loop."); bool close = await HandleAcceptExceptionAsync(ex).ConfigureAwait(false); if (close) { logger?.LogInformation("Closing accept loop because of exception."); break; } } } }
public async Task TestSimpleStreamAsync() { var readStream = new MemoryStream(); var writeStream = new MemoryStream(); var messageStream = new MessageStream <SimpleMessage>( new SimpleMessageDeserializer(), new SimpleMessageSerializer(), new StreamDuplexMessageStream(readStream, writeStream) ); await messageStream.OpenAsync().ConfigureAwait(false); // Write two messages await messageStream.WriteAsync(new SimpleMessage { Id = 1, Value = 2 }).ConfigureAwait(false); await messageStream.WriteAsync(new SimpleMessage { Id = 2, Value = 4 }).ConfigureAwait(false); await messageStream.CloseAsync().ConfigureAwait(false); // Reset the streams position so we can read in the messages readStream = new MemoryStream(writeStream.ToArray()); readStream.Position = 0; writeStream = new MemoryStream(); messageStream = new MessageStream <SimpleMessage>( new SimpleMessageDeserializer(), new SimpleMessageSerializer(), new StreamDuplexMessageStream(readStream, writeStream) ); var receivedMessages = new List <SimpleMessage>(); var closedTcs = new TaskCompletionSource <bool>(); var eventedMessageStream = new EventMessageStream <SimpleMessage> ( new SimpleMessageDeserializer(), new SimpleMessageSerializer(), new StreamDuplexMessageStream(readStream, writeStream), message => { receivedMessages.Add(message); return(new ValueTask <bool>()); }, () => // ignore keep alive. { return(new ValueTask()); }, (ex) => // The stream will close because the memory stream will run out of data so ignore results { closedTcs.TrySetResult(true); return(new ValueTask()); } ); await eventedMessageStream.OpenAsync().ConfigureAwait(false); await closedTcs.Task.ConfigureAwait(false); Assert.Equal(2, receivedMessages.Count); try { await eventedMessageStream.CloseAsync().ConfigureAwait(false); } catch (Exception ex) { // ignore, the EOF will have closed it. } }
public async Task SocketReadsData() { ValueTask HandleConnection(SocketServer <object, object> .Connection connection) { Logger.Info($"Client connected to server: {connection.Id}"); return(new ValueTask()); } ValueTask <bool> HandleServerMessage(SocketServer <object, object> .Connection connection, object message) { Logger.Info($"Server message received: {connection.Id}:{message}"); return(new ValueTask <bool>()); } ValueTask HandleServerDisconnection(SocketServer <object, object> .Connection connection, Exception ex, bool expected) { Logger.Info($"Client disconnected from server: {connection.Id}:{expected}. {ex}"); return(new ValueTask()); } ValueTask HandleServerKeepAlive(SocketServer <object, object> .Connection connection) { Logger.Info($"Server keep handled alive for: {connection.Id}"); return(new ValueTask()); } var server = new SocketServer <object, object>( Deserializer, Serializer, null, HandleConnection, HandleServerMessage, HandleServerDisconnection, HandleServerKeepAlive); await server.ListenAsync(5463).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false); ValueTask <bool> HandleClientMessage(object message) { Logger.Info($"Client message received: {message}"); return(new ValueTask <bool>()); } ValueTask HandleClientDisconnection(Exception ex) { Logger.Info($"Client disconnected"); return(new ValueTask()); } ValueTask HandleClientKeepAlive() { Logger.Info($"Handling client keep alive."); return(new ValueTask()); } var config = new SocketConfiguration { Ip = "127.0.0.1", Port = 5463 }; var clientStream = new EventMessageStream <object>( Deserializer, Serializer, new SocketDuplexMessageStreamWrapper(config), HandleClientMessage, HandleClientKeepAlive, HandleClientDisconnection); await clientStream.OpenAsync().ConfigureAwait(false); await clientStream.WriteAsync(new PingMessage { Id = 1 }).ConfigureAwait(false); }