chattle-sharp/Server/Connections.cs

94 lines
1.9 KiB
C#
Raw Normal View History

2024-10-06 14:08:17 +02:00
using System.Collections.Concurrent;
using System.Net.Sockets;
internal class Connections : IDisposable
{
private record struct User(TcpClient Tcp, Thread Thread);
private readonly ConcurrentDictionary<string, uint> _aliasUsages = [];
private readonly ConcurrentDictionary<string, User> _users = [];
public void Accept(TcpListener tcp)
{
try
{
var client = tcp.AcceptTcpClient();
new Thread(() => Listen(client)).Start();
}
catch (SocketException)
{
return;
}
}
private void Broadcast(TcpClient tcp, string alias, string action)
{
bool NotMe(User aliasUser) => aliasUser.Tcp != tcp;
var message = $"{alias} {action}";
Console.WriteLine(message);
foreach (var user in _users.Values.Where(NotMe))
{
var writer = new BinaryWriter(user.Tcp.GetStream());
writer.Write(message);
writer.Flush();
}
}
public void Dispose()
{
foreach (var user in _users.Values)
{
user.Tcp.Close();
user.Thread.Join();
user.Tcp.Dispose();
}
}
private void Listen(TcpClient tcp)
{
var reader = new BinaryReader(tcp.GetStream());
var requestedAlias = reader.ReadString();
var usages = _aliasUsages.GetOrAdd(requestedAlias, 0);
var alias = (usages == 0) ? requestedAlias : $"{requestedAlias} ({usages})";
_aliasUsages[requestedAlias] += 1;
if (!_users.TryAdd(alias, new User(tcp, Thread.CurrentThread)))
{
throw new InvalidOperationException("User with alias already exists");
}
Broadcast(tcp, alias, "has connected.");
while (true)
{
const int checkDelayMilliseconds = 100;
try
{
var message = reader.ReadString();
Broadcast(tcp, alias, $": {message}");
}
catch (EndOfStreamException)
{
break;
}
Thread.Sleep(checkDelayMilliseconds);
}
if (!_users.Remove(alias, out var _))
{
throw new InvalidOperationException("User with alias does not exist");
}
Broadcast(tcp, alias, "has disconnected.");
}
}