Помогите написать программу мониторинга сетевой активности компьютера на C#
Мне нужно написать программу для мониторинга сетевой активности
Ответы (1 шт):
Вы используете неверный метод считывания данных, т. е. polling (ссылка). Тут лучше подойдёт interrupting.
public async Task<UserInfo> GetRemoteInfoAsync()
{
// Обращение к серверу
}
Тут:
UserInfo- инфа об юзере (в которой, конечно, содержится нашUserStatus)GetRemoteInfoAsync- метод извлечения этой инфы с удалённого сервера
Исходя и этого нам нужны:
Формат клиент-сервер сообщений
Классы для клиента/сервера
Пахнет low-level работёнкой с сокетами (если вы действительно хотите понять, как это работает).
Реализация
Формат сообщений
Мне в голову пришли вот такие команды:
HELLO- команда создаёт эксклюзивный сокет для вашего клиента и возвращает его адрес вместе с версией сервера. Пример ответа:v1.0 56437QUIT- освобождает ваш ранее созданный сокет и завершает сеанс. Пример ответа:Good bye!INFO- возвращает инфу. Здесь ответ - сериализованный объектUserInfo
Хэлперы
public static class IpServices
{
IPAddress localIpAddress;
public IPAddress LocalIpAddress
{
get
{
localIpAddress ??= GetLocalIpAddress();
return localIpAddress;
}
}
static IPAddress GetLocalIpAddress()
{
var props = IPGlobalProperties.GetIPGlobalProperties();
return Dns.GetHostAddresses(props.HostName)[0];
}
public static FreePortEnumerable EnumerateFreePorts()
{
var props = IPGlobalProperties.GetIPGlobalProperties();
return new FreePortEnumerable(props);
}
public static int GetNextFreePort()
{
var freePortSeq = EnumerateFreePorts();
using var freePortIter = freePortSeq.GetEnumerator();
return freePortIter.MoveNext() ? freePortIter.Current : -1;
}
public readonly struct FreePortEnumerable : IEnumerable<int>
{
readonly IPEndPoint[] activeEndPoints;
internal FreePortEnumerable(IPGlobalProperties properties)
{
if (properties == null) throw new ArgumentNullException("properites");
activeEndPoints = props.GetActiveTcpListeners();
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<int>
{
readonly IEnumerator<IPEndPoint> activeEndPointIter;
public int Current { get; private set; }
internal Enumerator(FreePortEnumerable seq)
{
activeEndPointIter = seq.activeEndPoints.GetEnumerator();
Current = 1024;
}
public void Dispose() { }
public bool MoveNext()
{
while (!activeEndPointIter.MoveNext() ||
activeEndPointIter.Current != Current) { }
if (Current = ushort.MaxValue) return false;
Current++;
return true;
}
public void Reset()
{
activeEndPointIter.Reset();
Current = 1024;
}
}
}
}
public sealed class MyListenerPool
{
readonly Dictionary<int, MyListener> listeners; // чтоб GC не коллектил
public MyServer Server { get; }
public MyListener DefaultListener { get; }
internal MyListenerPool(MyServer server)
{
Server = server ?? throw new ArgumentNullException("server");
var defaultListenerPort = IpServices.GetNextFreePort();
if (defaultListenerPort == -1)
throw new SocketException((int)SocketError.TooManyOpenSockets);
DefautListener = new MyListener(new IPAddress(IpServices.LocalIpAddress, defaultListenerPort), true);
}
public bool TryRent(out MyListener listener)
{
#pragma warning disable CS86025
listener = null;
#pragma warning restore CS86025
var listenerPort = ipServices.GetNextFreePort();
if (listenerPort == -1)
return false;
else
{
listener = new MyListener(new IPAddress(IpServices.LocalIpAddress, listenerPort));
listener.RegisterListenCallback(Server.HandleRequest);
lock (syncRoot)
{
listeners.Add(listener.LocalEndPoint.Port, listener);
}
return true;
}
}
public MyListener Rent()
{
while (!TryRent(out var listener)) { }
return listener;
}
}
public sealed class MyListener : IDisposable
{
readonly object syncRoot = new object();
public Socket Socket { get; }
public IPEndPoint LocaEndPoint => Socket.LocalEndPoint as IPEndPoint;
public Socket? ClientSocket { get; private set; }
public IPEndPoint RemoteEndPoint
{
get
{
if (!IsConnected) throw new InvalidOperationException("Disconnected listeners have not remote endpoint");
return ClientSocket.RemoteEndPoint;
}
}
public bool IsConnected => ClientSocket != null;
internal MyListener(IPEndPoint localEndPoint, bool isDefault = false)
{
if (localEndPoint == null) throw new ArgumentNullException("endPoint");
Socket = new Socket(SocketType.Stream, ProtocolType.Ip);
Socket.Bind(localEndPoint);
Socket.Listen(isDefault ? 4096: 8);
}
public void Accept()
{
ClientSocket = Socket.Accept();
}
public bool Disconnect()
{
if (!IsConnected) return false;
ClientSocket.Close();
return true;
}
public bool TryReceiveRequest(out MyRequest request)
{
#pragma warning disable CS86025
request = null;
#pragma warning restore CS86025
var dataSegmentOwners = new ValueArrayBuilder<IMemoryOwner>(stackalloc IMemoryOwner[8]);
var dataBuilder = new ValueSequenceBuilder();
Span<byte> buffer = stackalloc byte[1024];
try
{
while (true)
{
var receivedBytes = Socket.Receive(buffer);
if (receivedBytes = 0) break;
var dataSegmentOwner = MemoryPool<byte>.Shared.Rent(receivedBytes);
var dataSegment = dataSegmentOwner.Memory;
dataSegmentOwners.Add(dataSegmentOwner);
dataBuilder.Append(dataSegment);
}
var data = dataBuilder.Build();
using var stream = SequenceStream(data);
request = new MyRequestSerializer().Deserialize(stream, out request);
}
finally
{
for (var i = 0; i < dataSegmentOwners.Length; i++)
dataSegmentOwners[i].Dispose();
}
return true;
}
public MyRequest ReceiveRequest()
{
while (!TryReceiveMessage(out var request)) { }
return request;
}
public Task ListenAsync(CancellationToken cancellationToken = default) => new Task(() =>
{
while (!cancellationToken.IsCancellationRequested)
{
if (!TryReceiveRequest(out var request)) continue;
lock (syncRoot)
{
foreach (var _callback in GetListenCallbacks())
_callback(request);
}
}
});
public void SendResponse(MyResponse response)
{
}
public ListenCallbackRegistation RegisterListenCallback(Action<MyRequest> callback)
{
// ...
}
public struct ListenCallbackRegistration : IDisposable
{
// ...
}
}
public sealed class MyRequest
{
public MyRequestCommandCode CommandCode { get; }
public ReadOnlyCollection<ReadOnlyMemory<char>> Args { get; }
// ...
}
public ref struct ValueSequenceBuilder<T>
{
// ...
}
public struct MyRequestSerializer
{
// Метод void Serialize(Stream stream, MyMessage message);
// Метод MyMessage Deserialize(Stream stream);
}
public sealed class SequenceStream : Stream
{
// Реализация через ReadOnlySequence
}
public sealed class MyResponse
{
// Ну, думаю вы поняли)
}
Асинхронные методы, а также методы диспозинга скипнуты. ValueXXX типы - облегчённые аналоги их не like-value типов.
Класс сервера
public sealed class MyServer
{
readonly Dictionary<ReadOnlyMemory<char>, UserInfo> users;
readonly object syncRoot = new object();
public MyListenerPool ListenerPool { get; }
public MyListener DefaultListener { get; }
public MyServer()
{
ListenerPool = new MyListenerPool(this);
users = new Dictionary<ReadOnlyMemory<char>, UserInfo>();
}
public void HandleRequest(MyListener listener, MyRequest request)
{
if (request == null) throw new ArgumentNullException("request");
switch (request.CommandCode)
{
MyRequestCommandCode.Hello:
var task = new Task(l =>
{
const string responseFormat = "";
var _listener = (MyListener)l;
var newListener = ListenerPool.Rent();
newListener.ListenAsync();
var response = new MyResponse(ValueString.Format(responseFormat, newListener.LocalEndPoint.Port));
_listener.SendResponse(response);
}, listener);
task.Start();
break;
MyRequestCommandCode.Quit:
listener.Disconnect();
break;
MyRequestCommandCode.Info:
UserInfo info;
lock (syncRoot)
{
info = users[request.Args[0]];
}
var task = new Task(l =>
{
// response sending (я уже устал)
});
break;
}
}
}
Класс клиента
Тут ещё проще чем с сервером. Я его не буду писать, т. к. вам нужно просто прочитать про сокеты.
ВАЖНО: Это всё ещё псевдо-код! Он всё ещё нуждается в дальнейшей доработке. Я постарался за 1 присест записать весь код. Внимательно проделайте его review (просто держите у себя в голове, что я тупой, если найдёте ошибку).