Answer the question
In order to leave comments, you need to log in
Why sometimes data over TCP reach correctly, and sometimes not?
Hello. I am writing a client-server application. One application (server) takes the necessary IP addresses, ports and directories with files from the configuration file, and then sends all the files in the directory to a specific endpoint. In the meantime, another application (the client) captures the server's connection and starts reading data from the NetworkStream . Each file is preceded by a kilobyte header (string) of the format: <file size>|<file name>|<"LAST" if the last one>. Sometimes (about 1 time in 8 gears) a problem occurs. The file size cannot be converted to a number, which may indicate that the bytes were not counted correctly or were corrupted.
I am catching the exception:
long fileSize = 0;
try
{
fileSize = Int64.Parse(headerElements[0]);
}
catch (Exception)
{
ConsoleMessenger.Error("Can't to parse header.");
break;
}
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();
}
}
}
Answer the question
In order to leave comments, you need to log in
ns.Read(headerBuffer); // прочитали заголовок
Here there is no guarantee that you have read exactly as many bytes as you have a buffer.
Therefore, the Read method returns the number of bytes actually read.
These are classic reading problems, which you can read about here
. It’s just about how to solve all this using pipelines, which Vasily Bannikov mentioned
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question