Здравствуйте. Пишу клиент-серверное приложение. Одно приложение (сервер) берёт из файла конфигурации нужные IP-адреса, порты и директории с файлами, а затем отправляет все файлы в директории на определённый эндпоинт. Тем временем другое приложение (клиент) фиксирует подключение сервера и начинает читать данные из
NetworkStream. Перед каждым файлом идёт килобайтный заголовок (строка) формата: <размер файла>|<имя файла>|<"LAST", если последний>. Иногда (примерно 1 раз к 8 передачам) происходит проблема. Размер файла не получается преобразовать в число, что может свидетельствовать о том, что байты считались некорректно, или были повреждены.
Я перехватываю исключение:
long fileSize = 0;
try
{
fileSize = Int64.Parse(headerElements[0]);
}
catch (Exception)
{
ConsoleMessenger.Error("Can't to parse header.");
break;
}
Что может быть не так? TCP ведь обеспечивает точную передачу данных без искажений. При том, эта ошибка возникает не всегда, в основном файлы передаются корректно.
Вот код сервера, который подключается к клиентам:
public class FileBroadcastingServer
{
private Socket _server;
private List<RemoteMonitor> _remoteMonitors;
public FileBroadcastingServer(List<RemoteMonitor> monitors)
{
_server = new Socket(SocketType.Stream, ProtocolType.Tcp);
_remoteMonitors = monitors;
}
public void Broadcast()
{
ConsoleMessenger.Info($"Broadcasting to {_remoteMonitors.Count} remote monitors");
foreach(RemoteMonitor monitor in _remoteMonitors)
{
string[] paths = Directory.GetFiles(monitor.FilesDirectory);
IPEndPoint endPoint = monitor.IPEndPoint;
SendFiles(endPoint, paths);
}
}
public void SendFiles(IPEndPoint endPoint, string[] paths)
{
try { _server.Connect(endPoint); }
catch(Exception)
{
ConsoleMessenger.UnableToConnect($"Unable to connect to {endPoint.Address}:{endPoint.Port}. Skipping. . .");
return;
}
NetworkStream ns = null;
try
{
ns = new NetworkStream(_server);
}
catch (Exception)
{
ConsoleMessenger.Error("Unable to get network stream. Skipping. . .");
_server.Close();
return;
}
for(int i = 0; i < paths.Length; i++)
{
string path = paths[i];
FileInfo fileInfo = new FileInfo(path);
string header = fileInfo.Length + "|" + fileInfo.Name; // формируем заголовок
if (i == paths.Length - 1)
header += "|LAST"; // если файл последний в очереди, то отправить тег
byte[] headerBuffer = new byte[1024]; // буфер заголовка
Encoding.ASCII.GetBytes(header, headerBuffer);
byte[] fileBuffer = new byte[fileInfo.Length]; // файловый буфер
using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
fs.Read(fileBuffer);
}
try
{
ns.Write(headerBuffer); // записываем в поток заголовок
ns.Write(fileBuffer); // записываем в потоке файл
}
catch (Exception)
{
ns.Close();
_server.Close();
ConsoleMessenger.Error("Unable to write data into the network stream. Skipping. . .");
return;
}
ConsoleMessenger.Success($"File {fileInfo.Name} has been sended!");
Task.Delay(2000);
}
ConsoleMessenger.Success("Dispatch finished!");
ns.Close();
_server.Close();
}
}
Вот код клиента, принимающего файлы от сервера:
public class Receiver
{
private Socket _receiver;
private string _savingDirectory;
private IPEndPoint _ipEndPoint;
private Socket _handler;
public Receiver(Socket receiver, IPEndPoint ipEndPoint, string savingDirectory)
{
_receiver = receiver;
_savingDirectory = savingDirectory;
_ipEndPoint = ipEndPoint;
}
public void ReceiveFiles()
{
_receiver.Bind(_ipEndPoint);
while (true)
{
_receiver.Listen(10);
_handler = _receiver.Accept();
ConsoleMessenger.Success("Connected!");
NetworkStream ns = new NetworkStream(_handler);
byte[] headerBuffer = new byte[1024];
bool isLast = false;
while (!isLast)
{
Task.Delay(2000);
ns.Read(headerBuffer); // прочитали заголовок
string header = Encoding.ASCII.GetString(headerBuffer);
string[] headerElements = header.Split('|');
if (headerElements.Length == 3)
isLast = true;
// Уязвимое место! --------------------
long fileSize = 0;
try
{
fileSize = Int64.Parse(headerElements[0]);
}
catch (Exception)
{
ConsoleMessenger.Error("Can't to parse header.");
break;
}
// -------------------------------------
string fileName = headerElements[1];
byte[] fileBuffer = new byte[fileSize];
ns.Read(fileBuffer);
string savingDir = _savingDirectory;
char[] invalidCharacters = Path.GetInvalidPathChars();
if (!savingDir.EndsWith('/'))
savingDir += "/";
savingDir += fileName;
string savingFileDir = new string(savingDir.Where(x => !invalidCharacters.Contains(x)).ToArray());
using (FileStream fs = new FileStream(savingFileDir, FileMode.OpenOrCreate, FileAccess.Write))
{
fs.Write(fileBuffer);
}
ConsoleMessenger.Success($"File {fileName} has been received!");
}
ConsoleMessenger.Info("Waiting for connections. . .");
ns.Close();
}
}
}
Прошу помочь и объяснить.