Перемешанный байтовый массив после использования TcpClient и TcpListener - PullRequest
0 голосов
/ 09 марта 2010

Я хочу использовать TcpClient и TcpListener для отправки mp3-файла по сети. Я реализовал решение этой проблемы с использованием сокетов, но возникли некоторые проблемы, поэтому я изучаю новый / лучший способ отправки файла.

Я создаю байтовый массив, который выглядит следующим образом: length_of_filename | Имя файла | файл

Это должно быть передано с использованием вышеупомянутых классов, но на стороне сервера массив байтов, который я прочитал, полностью испорчен, и я не уверен почему.

Способ отправки:

 public static void Send(String filePath)
    {
        try
        {
            IPEndPoint endPoint = new IPEndPoint(Settings.IpAddress, Settings.Port + 1);
            Byte[] fileData = File.ReadAllBytes(filePath);
            FileInfo fi = new FileInfo(filePath);

            List<byte> dataToSend = new List<byte>();
            dataToSend.AddRange(BitConverter.GetBytes(Encoding.Unicode.GetByteCount(fi.Name))); // length of filename
            dataToSend.AddRange(Encoding.Unicode.GetBytes(fi.Name)); // filename
            dataToSend.AddRange(fileData); // file binary data


            using (TcpClient client = new TcpClient())
            {
                client.Connect(Settings.IpAddress, Settings.Port + 1);

                // Get a client stream for reading and writing.
                using (NetworkStream stream = client.GetStream())
                {
                    // server is ready 
                    stream.Write(dataToSend.ToArray(), 0, dataToSend.ToArray().Length);
                }
            }

        }
        catch (ArgumentNullException e)
        {
            Debug.WriteLine(e);
        }
        catch (SocketException e)
        {
            Debug.WriteLine(e);
        }
    }
}

Тогда на стороне сервера это выглядит следующим образом:

    private void Listen()
    {
        TcpListener server = null;
        try
        {
            // Setup the TcpListener
            Int32 port = Settings.Port + 1;
            IPAddress localAddr = IPAddress.Parse("127.0.0.1");

            // TcpListener server = new TcpListener(port);
            server = new TcpListener(localAddr, port);

            // Start listening for client requests.
            server.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[1024];
            List<byte> data;

            // Enter the listening loop.
            while (true)
            {
                Debug.WriteLine("Waiting for a connection... ");
                string filePath = string.Empty;

                // Perform a blocking call to accept requests.
                // You could also user server.AcceptSocket() here.
                using (TcpClient client = server.AcceptTcpClient())
                {
                    Debug.WriteLine("Connected to client!");
                    data = new List<byte>();

                    // Get a stream object for reading and writing
                    using (NetworkStream stream = client.GetStream())
                    {
                        // Loop to receive all the data sent by the client.
                        while ((stream.Read(bytes, 0, bytes.Length)) != 0)
                        {
                            data.AddRange(bytes);
                        }
                    }
                }

                int fileNameLength = BitConverter.ToInt32(data.ToArray(), 0);
                filePath = Encoding.Unicode.GetString(data.ToArray(), 4, fileNameLength);
                var binary = data.GetRange(4 + fileNameLength, data.Count - 4 - fileNameLength);

                Debug.WriteLine("File successfully downloaded!");

                // write it to disk
                using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Append)))
                {
                    writer.Write(binary.ToArray(), 0, binary.Count);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex);
        }
        finally
        {
            // Stop listening for new clients.
            server.Stop();
        }
    }

Кто-нибудь может увидеть что-то, что я пропускаю / делаю неправильно?

1 Ответ

4 голосов
/ 09 марта 2010

Повреждение вызвано следующим кодом на сервере:

// Loop to receive all the data sent by the client.
while ((stream.Read(bytes, 0, bytes.Length)) != 0)
{
    data.AddRange(bytes);
}

stream.Read не всегда будет заполнять буфер bytes. Он не будет заполнен, если в сокете TCP больше нет доступных данных или при чтении последнего фрагмента сообщения (если он не является точным кратным размеру буфера).

Вызов data.AddRange добавляет все из bytes (при условии, что он всегда заполнен). В результате это иногда приводит к добавлению данных из предыдущего вызова в stream.Read. Чтобы исправить это, вам нужно сохранить количество байтов, возвращаемых Read, и добавить только это количество байтов:

int length;

while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)
{
    var copy = new byte[length];
    Array.Copy(bytes, 0, copy, 0, length);
    data.AddRange(copy);
}

Обратите внимание, что вы можете реструктурировать свой код для повышения производительности и использования памяти (и, возможно, облегчить чтение в результате). Вместо того, чтобы читать все данные в память на клиенте перед отправкой, вы можете просто написать прямо в NetworkStream. На сервере вам не нужно копировать все данные из потока в память. Вы можете считать 4-байтовую длину имени файла и декодировать его, затем прочитать и декодировать имя файла и, наконец, скопировать оставшуюся часть потока непосредственно в FileStream (BinaryWriter не требуется).

Стоит также отметить, что вы создаете выходной файл с FileMode.Append. Это означает, что каждый отправленный файл будет добавлен к предыдущей копии с тем же именем. Вместо этого вы можете использовать FileMode.Create, что перезапишет, если файл уже существует.

...