Гарантии протокола TCP в рамках .NET
Есть один вопрос, который мне не дает покоя. Есть GPS-tracker, передающий данные по TCP/IP, используя свой протокол. Пишу background service (.NET Core 3.1), который должен слушать tcp-порт и сохранять пакеты сообщений в базу в сыром виде (byte[] или закодированные в строку). Сейчас мой код прослушки tcp-порта выглядит примерно так:
public class TcpListenerBackgroundService : BackgroundService
{
private readonly ITcpListenerWrapper _tcpListener;
private readonly ILogger<TcpListenerBackgroundService> _logger;
public TcpListenerBackgroundService(ITcpListenerWrapper tcpListener, ILogger<TcpListenerBackgroundService> logger)
{
_tcpListener = tcpListener;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _tcpListener.StartListeningAsync(stoppingToken);
}
}
TcpListenerWrapper.cs
public sealed class TcpListenerWrapper : ITcpListenerWrapper, IDisposable
{
private const int BufferSize = 1024;
private readonly TcpListener _listener;
private readonly IMessageManager _messageManager;
public TcpListenerWrapper(ITcpListenerWrapperConfiguration config, IMessageManager messageManager)
{
_listener = new TcpListener(IPAddress.Any, config.PortNumber);
_messageManager = messageManager;
}
public async Task StartListeningAsync(CancellationToken stoppingToken)
{
_listener.Start();
while (!stoppingToken.IsCancellationRequested)
{
var client = await _listener.AcceptTcpClientAsync();
var stream = client.GetStream();
while (!stoppingToken.IsCancellationRequested)
{
var buffer = new byte[BufferSize];
using (var ms = new MemoryStream())
{
int numBytesRead;
while ((numBytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, numBytesRead);
}
if (ms.Length > 0)
{
var messageAsText = Encoding.ASCII.GetString(buffer);
// Console.WriteLine($"New message received: {messageAsText}");
var message = _messageManager.CreateMessage(buffer);
message.Save(); // Сохранение в бд.
}
}
}
}
}
public void Dispose()
{
_listener.Stop();
}
}
Пусть GPS-tracker послал 10 пакетов с периодом в 1 минуту.
Вопрос: Может ли быть так, что в NetworkStream окажется сразу несколько пакетов (скажем, 3 пакета)? Или есть гарантия, что они будут приходить по одному?
Доп. вопрос: Есть ли в коде прослушки сообщений какие-то ошибки? Может, здесь чего-то не хватает, либо наоборот, лишнее?
Ответы (1 шт):
Вопрос: Может ли быть так, что в NetworkStream окажется сразу несколько пакетов (скажем, 3 пакета)? Или есть гарантия, что они будут приходить по одному?
.NET ничего не знает (и не должен) о том, что там какой то GPS треккер передает данные. Как придут байты, так и будут сложены. То есть, в теории может прийти вначале два с половиной пакета, потом ещё полтора. А может и по другому. TCP оперирует байтами. И протокол гарантирует только что байты (БАЙТЫ!) придут в том же порядке, что не будет "случайно пропущенного байта/бита". Также протокол будет пытаться доставить пакеты или сделает "разрыв соединения", если доставить невозможно (провод перерезали).
Посмотрел в код. Вы каждый пришедший пакет пытаетесь декодировать как полностью пришедший пакет. Но только это гарантируется. Да, при локальном тесте оно скорее всего обычно так и будет работать. Но как только винда решит приуныть (антивирусник запустился) или сеть за роутером/gsm модулем. может быть все что угодно.
У меня был случай, когда из за одного известного фаервола всегда вначале первым чтением приходил 1 байт. А потом все остальное как обычно. Но делал он это не постоянно. А программист в коде рассчитывал, что ну хоть 4 байта придется сразу. А нет...
Что делать в коде. Скорее всего в этом протоколе разделителем сообщений есть перевод строки. Поэтому, пришли данные, добавили к конец буфера, потом ищите перевод строки. Есть - извлекаем пакет, повторяем поиск. нет - до следующего пакета.