94 lines
1.9 KiB
C#
94 lines
1.9 KiB
C#
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.");
|
|
}
|
|
} |