Skip to content

Commit

Permalink
Day 11: Higher-level API.
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenCleary committed Mar 5, 2022
1 parent 97918f1 commit 4d52ad1
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 12 deletions.
47 changes: 46 additions & 1 deletion Chat/ChatApi/ChatConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
Expand All @@ -21,6 +22,7 @@ public sealed class ChatConnection
private readonly Channel<IMessage> _inputChannel;
private readonly Channel<IMessage> _outputChannel;
private readonly Timer _timer;
private readonly ConcurrentDictionary<Guid, TaskCompletionSource> _outstandingRequests = new();

public ChatConnection(PipelineSocket pipelineSocket, TimeSpan keepaliveTimeSpan = default)
{
Expand All @@ -40,6 +42,20 @@ public ChatConnection(PipelineSocket pipelineSocket, TimeSpan keepaliveTimeSpan

public void Complete() => _outputChannel.Writer.Complete();

public Task SetNicknameAsync(string nickname)
{
var setNicknameRequestMessage = new SetNicknameRequestMessage(Guid.NewGuid(), nickname);
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_outstandingRequests.TryAdd(setNicknameRequestMessage.RequestId, tcs);
return SendMessageAndWaitForResponseAsync();

async Task SendMessageAndWaitForResponseAsync()
{
await SendMessageAsync(setNicknameRequestMessage);
await tcs.Task;
}
}

public async Task SendMessageAsync(IMessage message)
{
await _outputChannel.Writer.WriteAsync(message);
Expand Down Expand Up @@ -102,7 +118,36 @@ private async void PipelineToChannelAsync()
var data = await _pipelineSocket.InputPipe.ReadAsync();

foreach (var message in ParseMessages(data.Buffer))
await _inputChannel.Writer.WriteAsync(message);
{
if (message is KeepaliveMessage)
{
// Ignore
}
else if (message is AckResponseMessage ackResponseMessage)
{
if (!_outstandingRequests.TryRemove(ackResponseMessage.RequestId, out var tcs))
{
// The server sent us an ack response for a request we didn't send.
throw new InvalidOperationException("Protocol violation.");
}

tcs.TrySetResult();
}
else if (message is NakResponseMessage nakResponseMessage)
{
if (!_outstandingRequests.TryRemove(nakResponseMessage.RequestId, out var tcs))
{
// The server sent us an ack response for a request we didn't send.
throw new InvalidOperationException("Protocol violation.");
}

tcs.TrySetException(new Exception("Request was rejected."));
}
else
{
await _inputChannel.Writer.WriteAsync(message);
}
}

if (data.IsCompleted)
{
Expand Down
5 changes: 5 additions & 0 deletions Chat/ChatApi/MessageSerialization.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public static int GetMessageLengthPrefixValue(IMessage message)
message = new BroadcastMessage(from, text);
return true;
}
else if (messageType == 2)
{
message = new KeepaliveMessage();
return true;
}
else if (messageType == 3)
{
if (!sequenceReader.TryReadGuid(out var requestId))
Expand Down
22 changes: 11 additions & 11 deletions Chat/ChatClient/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ await foreach (var message in chatConnection.InputMessages)
{
Chat.Text += $"{broadcastMessage.From}: {broadcastMessage.Text}\n";
}
else if (message is AckResponseMessage ackResponseMessage)
{
Log.Text += $"Got ack from {chatConnection.RemoteEndPoint} for request {ackResponseMessage.RequestId}.\n";
}
else if (message is NakResponseMessage nakResponseMessage)
{
Log.Text += $"Got nak from {chatConnection.RemoteEndPoint} for request {nakResponseMessage.RequestId}.\n";
}
else
Log.Text += $"Got unknown message from {chatConnection.RemoteEndPoint}.\n";
}
Expand All @@ -103,9 +95,17 @@ private async void Button_Click_3(object sender, RoutedEventArgs e)
}
else
{
var message = new SetNicknameRequestMessage(Guid.NewGuid(), nicknameTextBox.Text);
await _chatConnection.SendMessageAsync(message);
Log.Text += $"Sent nickname request {message.RequestId} for {message.Nickname}\n";
var nickname = nicknameTextBox.Text;
try
{
Log.Text += $"Sending nickname request for {nickname}\n";
await _chatConnection.SetNicknameAsync(nickname);
Log.Text += $"Successfully set nickname to {nickname}\n";
}
catch (Exception ex)
{
Log.Text += $"Unable to set nickname to {nickname}: {ex.Message}\n";
}
}
}
}
Expand Down

0 comments on commit 4d52ad1

Please sign in to comment.