Отправка файла с использованием TCPClient и NetworkStream в C # - PullRequest
4 голосов
/ 01 апреля 2012

Я пытался отправить файл с клиента на серверное приложение, используя класс TCPClient в C #. Перед отправкой фактических данных я отправляю некоторую дополнительную информацию, такую ​​как точный размер файла и имя файла, чтобы серверное приложение знало, сколько еще нужно прочитать. Самое смешное, что все было в порядке, когда я тестировал его на 127.0.0.1 - как только я заменил IP-адрес реальным, сервер смог прочитать только около 1,5 КБ отправленных данных. Он по-прежнему получает имя файла и размер файла, но нет никакого способа получить реальные данные.

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

Кто-нибудь получил идею? Ура! * * 1005

Edit:

Спасибо, пока что это то, что я получил по кодам. Для клиента:

        IPAddress ipAddress = IPAddress.Parse("xx.xx.xx.xx");
        int port = 3003;
        int bufferSize = 1024;

        TcpClient client = new TcpClient();
        NetworkStream netStream;

        // Connect to server
        try
        {
            client.Connect(new IPEndPoint(ipAddress, port));

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        netStream = client.GetStream();

        // Read bytes from image
        byte[] data = File.ReadAllBytes("C:\\Users\\Dan\\Desktop\\asdf.jpg");

        // Build the package
        byte[] dataLength = BitConverter.GetBytes(data.Length);
        byte[] package = new byte[4 + data.Length];
        dataLength.CopyTo(package, 0);
        data.CopyTo(package, 4);

        // Send to server
        int bytesSent = 0;
        int bytesLeft = package.Length;

        while (bytesLeft > 0)
        {

            int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;

            netStream.Write(package, bytesSent, nextPacketSize);
            bytesSent += nextPacketSize;
            bytesLeft -= nextPacketSize;

        }

        // Clean up
        netStream.Close();
        client.Close();

И сервер:

        TcpListener listen = new TcpListener(3003);
        TcpClient client;
        int bufferSize = 1024;
        NetworkStream netStream;
        int bytesRead = 0;
        int allBytesRead = 0;

        // Start listening
        listen.Start();

        // Accept client
        client = listen.AcceptTcpClient();
        netStream = client.GetStream();

        // Read length of incoming data
        byte[] length = new byte[4];
        bytesRead = netStream.Read(length, 0, 4);
        int dataLength = BitConverter.ToInt32(length,0);

        // Read the data
        int bytesLeft = dataLength;
        byte[] data = new byte[dataLength];

        while (bytesLeft > 0)
        {

            int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;

            bytesRead = netStream.Read(data, allBytesRead, nextPacketSize);
            allBytesRead += bytesRead;
            bytesLeft -= bytesRead;

        }

        // Save image to desktop
        File.WriteAllBytes("C:\\Users\\Dan\\Desktop\\tcpimage.jpg", data);

        // Clean up
        netStream.Close();
        client.Close();

Ответы [ 3 ]

2 голосов
/ 01 апреля 2012

Около 1,5 КиБ звучит как 1500 байт, «самый большой разрешенный Ethernet на сетевом уровне». Это максимальная единица передачи (mtu) , заставляющая ваш сетевой стек разбивать ваш файл на несколько небольших пакетов.

Вам нужно вызвать NetworkStream.Read в цикле, чтобы прочитать каждый полученный пакет. Пример кода этого на MSDN.

Объедините это с поведением по умолчанию .NET; объединение небольших пакетов для уменьшения количества отправленных пакетов, и вы также увидите такое поведение при отправке небольших пакетов. Это можно контролировать с помощью ServicePointManager.UseNagleAlgorithm или с помощью меньших параметров сокета с областью действия.

0 голосов
/ 14 июля 2017

Хорошо, не знаю, что я здесь делаю, но в случае, если кто-то использует это как ссылку.

Я избавился от ненужного копирования и сделал очень важное улучшение. Ваш способ вычислить nextPacketSize не завершен, поскольку может быть меньше данных, чем доступно 1024, и вы получите дополнительные нули в промежутке между этими кусками (у меня было много головной боли с этим, теперь я так рад это выяснить)

Я сделал их как функцию, необходимую для нескольких файлов, поэтому вот код:

клиент

это почти то же самое

public void sendData(byte[] data, NetworkStream stream)
{
    int bufferSize = 1024;

    byte[] dataLength = BitConverter.GetBytes(data.Length);

    stream.Write(dataLength, 0, 4);

    int bytesSent = 0;
    int bytesLeft = data.Length;

    while (bytesLeft > 0)
    {
        int curDataSize = Math.Min(bufferSize, bytesLeft);

        stream.Write(data, bytesSent, curDataSize);

        bytesSent += curDataSize;
        bytesLeft -= curDataSize;
    }
}

Сервер

public byte[] getData(TcpClient client)
{
    NetworkStream stream = client.GetStream();

    byte[] fileSizeBytes = new byte[4];
    int bytes = stream.Read(fileSizeBytes, 0, 4);
    int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);

    int bytesLeft = dataLength;
    byte[] data = new byte[dataLength];

    int bufferSize = 1024;
    int bytesRead = 0;

    while (bytesLeft > 0)
    {
        int curDataSize = Math.Min(bufferSize, bytesLeft);
        if (client.Available < curDataSize)
            curDataSize = client.Available; //This saved me

        bytes = stream.Read(data, bytesRead, curDataSize);

        bytesRead += curDataSize;
        bytesLeft -= curDataSize;
    }

    return data;
}
0 голосов
/ 24 сентября 2012

Я использовал часть вашего кода для тестового сетевого проекта, над которым я работаю. Я настроил пару вещей, чтобы они соответствовали требованиям моего проекта, но одно изменение, которое мне нужно было внести в исходный код, это добавить «-4» в строку, в которой была установлена ​​переменная bytesLeft. После этого код работал. Мой тестовый файл имеет размер 52 КБ и был успешно передан.

      // Read the data
    int bytesLeft = dataLength-4;
    byte[] data = new byte[dataLength];
...