I'm trying to implement a TcpListener using the TPL. I'm not sure If I'm doing everything correctly though. I'm trying to keep the code as small as possible as well. It all works fine in a console application.
public async Task RunAsync(CancellationToken cancellationToken)
{
var listener = new TcpListener(ip, port);
listener.Start();
while (!cancellationToken.IsCancellationRequested)
{
await AcceptClientAsync(listener, encoding, progress, cancellationToken);
}
listener.Stop();
}
Here is the AcceptClientAsync
method. Sorry for the weird formatting. StyleCop likes to do that..
private async Task AcceptClientAsync(TcpListener tcpListener, Encoding encoding, IProgress<string> progress, CancellationToken cancellationToken)
{
var client = await tcpListener.AcceptTcpClientAsync();
this.Connection = new ConnectionState { TcpClient = client };
while (client.Connected && !cancellationToken.IsCancellationRequested)
{
await
this.ReadStringAsync(this.Connection, encoding, progress)
.ContinueWith(
task =>
progress.Report(
string.Format("Client {0} disconnected.", Connection.TcpClient.Client.RemoteEndPoint)),
TaskContinuationOptions.OnlyOnFaulted);
}
}
And the ReadStringAsync
method.
private async Task ReadStringAsync(ConnectionState connection, Encoding encoding, IProgress<string> progress)
{
var stream = connection.TcpClient.GetStream();
if (connection.Read > 0)
{
var encoded = encoding.GetString(connection.Buffer, 0, connection.Read);
connection.StringBuilder.Append(encoded);
}
var decoded = connection.StringBuilder.ToString();
if (decoded.Contains("<End>"))
{
progress.Report(decoded.Replace("<End>", string.Empty));
}
connection.Read = await stream.ReadAsync(connection.Buffer, 0, connection.Buffer.Length);
}
EDIT:
Also, I'd like to keep using the IProgress<>
interface and support cancellation via CancellationToken
s
Update:
public class TcpServer
{
public TcpServer(Encoding encoding, IProgress<string> progress)
{
this.Clients = new BindingList<ConnectionState>();
this.Encoding = encoding;
this.Progress = progress;
}
public BindingList<ConnectionState> Clients { get; private set; }
private Encoding Encoding { get; set; }
private IProgress<string> Progress { get; set; }
public async Task WriteStringAsync(ConnectionState connection, string text, Encoding encoding)
{
var stream = connection.TcpClient.GetStream();
var data = encoding.GetBytes(text);
await stream.WriteAsync(data, 0, data.Length);
}
public async Task ConnectAsync(IPAddress ip, int port, Encoding encoding, IProgress<string> progress, CancellationToken token)
{
var tcpClient = new TcpClient();
await tcpClient.ConnectAsync(ip, port);
this.Progress.Report(string.Format("Successfully connected to {0}.", tcpClient.Client.RemoteEndPoint));
var connection = new ConnectionState { TcpClient = tcpClient };
this.Clients.Add(connection);
this.HandleClientAsync(tcpClient, encoding, progress, token);
}
public async Task RunAsync(IPAddress ip, int port, CancellationToken cancellationToken)
{
if (ip == null)
{
throw new ArgumentNullException("ip");
}
if (port > 65535)
{
throw new ArgumentOutOfRangeException("port");
}
var listener = new TcpListener(ip, port);
listener.Start();
this.Progress.Report(string.Format("Server now running on {0}.", listener.LocalEndpoint));
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
listener.Stop();
cancellationToken.ThrowIfCancellationRequested();
return;
}
var client = await listener.AcceptTcpClientAsync();
this.Progress.Report(string.Format("Client {0} connected.", client.Client.RemoteEndPoint));
this.HandleClientAsync(client, this.Encoding, this.Progress, cancellationToken);
}
}
private async Task ReadStringAsync(ConnectionState connection, Encoding encoding)
{
var stream = connection.TcpClient.GetStream();
if (connection.Read > 0)
{
connection.StringBuilder.Append(encoding.GetString(connection.Buffer, 0, connection.Read));
}
var decoded = connection.StringBuilder.ToString();
if (decoded.Contains("<End>"))
{
this.Progress.Report(
string.Format("Client {0}: {1}", connection.IpAddress, decoded.Replace("<End>", string.Empty)));
connection.StringBuilder.Clear();
}
connection.Read = await stream.ReadAsync(connection.Buffer, 0, connection.Buffer.Length);
}
private async Task HandleClientAsync(
TcpClient client,
Encoding encoding,
IProgress<string> progress,
CancellationToken cancellationToken)
{
var connection = new ConnectionState { TcpClient = client };
this.Clients.Add(connection);
while (!cancellationToken.IsCancellationRequested)
{
try
{
await this.ReadStringAsync(connection, encoding);
}
catch (SocketException)
{
progress.Report(
string.Format("Client {0} disconnected.", connection.TcpClient.Client.RemoteEndPoint));
}
}
}
}