Получите полные данные - PullRequest
0 голосов
/ 06 декабря 2018

Я написал небольшую программу, которая получает огромную строку по TCP.

Я могу читать из сетевого потока только один раз, и затем программа падает.Как я могу решить эту проблему или вы можете предложить мне лучший способ отправить большую строку по TCP.Размер буфера клиента составляет 202601176 (длина строки в байтах).

Это мой код:

namespace File_Transfer_Server
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener serverListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 6666);
            serverListener.Start();
            TcpClient tcpClient = serverListener.AcceptTcpClient();
            Console.WriteLine(">>> Receiving");

            byte[] clientBuffer = new byte[1024];
            Console.WriteLine(clientBuffer.Length);
            using (NetworkStream clientNStream = tcpClient.GetStream())
            {

                int i;
                string received = "";

                while ((i = clientNStream.Read(clientBuffer, 0, clientBuffer.Length)) > 0) //exception
                {
                    string data = Encoding.ASCII.GetString(clientBuffer, 0, i);
                    received += data;
                    Console.WriteLine(data);
                }
                File.WriteAllText(@"E:\receivedData.txt", received);
                Console.WriteLine(">>>Done");
            }
        }
    }
}

Это исключение:

    Unhandled Exception: System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   --- End of inner exception stack trace ---
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at File_Transfer_Server.Program.Main(String[] args) in D:\Files from PC\Visual Basic Projects - =&+Ivan+&=\Tesseract\Temp\File Transfer\File_Transfer_Server\Program.cs:line 29

РЕДАКТИРОВАТЬ:

Код клиента:

namespace File_Transfer_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpClient client = new TcpClient();
            client.Connect("127.0.0.1", 6666);

            NetworkStream clientNetworkStream = client.GetStream();

            string fileContent = FileBase64Encoding(@"D:\Download\AtomSetup-x64.exe");

                        byte[] fileBytes = Encoding.ASCII.GetBytes(fileContent);
            // byte[] fileLength = Encoding.ASCII.GetBytes(fileBytes.Length.ToString());
            Console.WriteLine(fileBytes.Length);
            clientNetworkStream.Write(fileBytes, 0, fileBytes.Length);
            Console.WriteLine("Send");
        }

        static string FileBase64Encoding(string path)
        {
            byte[] fileBytes = File.ReadAllBytes(path);
            string fileBase64String = Convert.ToBase64String(fileBytes);

            return fileBase64String;
        }
    }
}

1 Ответ

0 голосов
/ 07 декабря 2018

Проблема с вашим кодом заключается в том, что клиент закрывает соединение до того, как серверу удастся получить все данные.Вы должны сообщить серверу о длине вашего сообщения.Это называется «Фрейминг сообщения».

Решение

  1. Запишите длину сообщения перед его содержимым и прочитайте его на стороне сервера.
  2. Выделите буфер на сервере с размером длины сообщения.
  3. Чтение содержимого сообщения.

Необязательный шаг: чтобы убедиться, что у нас не будет никаких исключений Socket.клиент ждет сервера, пока он не получит все данные.Сервер закрывает соединение.

Код клиента:

static void Main(string[] args)
{
    TcpClient client = new TcpClient();
    client.Connect("127.0.0.1", 6666);

    NetworkStream clientNetworkStream = client.GetStream();

    string fileContent = FileBase64Encoding(@"D:\Download\AtomSetup-x64.exe");

    byte[] fileBytes = Encoding.ASCII.GetBytes(fileContent);
    // byte[] fileLength = Encoding.ASCII.GetBytes(fileBytes.Length.ToString());
    Console.WriteLine(fileBytes.Length);

    // Write the length of a message
    var integerBytes = BitConverter.GetBytes(fileBytes.Length); // integer has 4 bytes
    clientNetworkStream.Write(integerBytes, 0, integerBytes.Length);

    // Write the contents
    clientNetworkStream.Write(fileBytes, 0, fileBytes.Length);
    Console.WriteLine("Send");

    // Wait for server to finish receiving
    clientNetworkStream.ReadByte();
    Console.WriteLine("Connection closed");
}

static string FileBase64Encoding(string path)
{
    byte[] fileBytes = File.ReadAllBytes(path);
    string fileBase64String = Convert.ToBase64String(fileBytes);

    return fileBase64String;
}

Код сервера:

class Program
{
    static void Main(string[] args)
    {
        TcpListener serverListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 6666);
        serverListener.Start();
        TcpClient tcpClient = serverListener.AcceptTcpClient();
        Console.WriteLine(">>> Receiving");

        byte[] clientBuffer;
        using (NetworkStream clientNStream = tcpClient.GetStream())
        {

            int i;
            string received = "";

            byte[] integerBytes = new byte[sizeof(int)];
            clientNStream.Read(integerBytes, 0, integerBytes.Length); // receive message length
            int messageLength = BitConverter.ToInt32(integerBytes, 0);

            Console.WriteLine("Received message length: {0}", messageLength);

            clientBuffer = new byte[messageLength]; // allocate buffer

            clientNStream.Read(clientBuffer, 0, clientBuffer.Length); // read string contents

            Console.WriteLine("Received all the data");

            // we're done
            Console.WriteLine("Closing connection");
            tcpClient.Close();

            received = Encoding.ASCII.GetString(clientBuffer, 0, clientBuffer.Length);

            File.WriteAllText(@"E:\receivedData.txt", received);
            Console.WriteLine(">>>Done");
        }
    }
}

Как вы можетеувидеть код для сервера теперь проще.

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

...