A
A
Anton Baryshev2021-07-28 11:43:35
.NET
Anton Baryshev, 2021-07-28 11:43:35

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;
                    }

What could be wrong? TCP provides accurate data transmission without distortion. Moreover, this error does not always occur, mostly the files are transferred correctly.
Here is the server code that connects to the clients:
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();
        }
    }


Here is the client code that receives files from the server:
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();
            }
        }
    }


Please help and explain.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
Ilya, 2021-07-28
@AVollane

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 question

Ask a Question

731 491 924 answers to any question