отправка двоичных файлов через сокеты TCP c

#c# #c #c #sockets #tcp

#c# #c #c #сокеты #tcp

Вопрос:

Я создал клиент и сервер, которые устанавливают TCP-соединение через сокеты, я пытаюсь отправить двоичные данные через сокет, но я мог отправлять только файлы txt или pdf, мне не повезло с exe-файлами, я использую fread и fseek для чтения файла и разделения его в буфер. Когда я читаю весь exe-файл, он успешно отправляется, но когда я его разделяю, он отправляется поврежденным!

Я прочитал несколько книг о сокетах, но я все еще мало знаю, и у меня есть несколько вопросов.

можно ли отправлять весь файл одним send ()? или я должен продолжать отправлять его небольшими порциями?

Кроме того, почему файлы exe повреждаются, когда я отправляю их частями?

Спасибо!

Клиентский код (на c):

 int bufSize = 10000;
int sentBytes = 0;


  FILE * pFile;
  long remainingBytes;
  char * buffer;
  size_t resu<

  pFile = fopen ( "D:\file.exe" , "rb" );
  fseek (pFile , 0 , SEEK_END);
  remainingBytes = ftell (pFile);
  rewind (pFile);

int bufferSize = remainingBytes > bufSize ? bufSize : remainingBytes;
buffer = (char*) malloc (sizeof(char)*bufferSize);
send(Socket, (char*)amp;remainingBytes, 4, 0);

while(remainingBytes > 0)
{
    fseek (pFile , sentBytes , SEEK_SET);
    result = fread(buffer,1,bufferSize,pFile);
    if(bufferSize < remainingBytes)
    {
        send(Socket, buffer, bufferSize, 0);
    }
    else
    {
        send(Socket, buffer, remainingBytes, 0);
        bufferSize = remainingBytes;
    }
    remainingBytes -= bufferSize;
    sentBytes  = bufferSize;
}
  

Серверный код (на c #)

         try
        {
            int bufferSize = 200;
            int len = 0;
            int receivedBytes = 0;
            int remainingBytes = len;
            byte[] length = new byte[4];
            //byte[] imgBuf = new byte[bufferSize];

            int current = 0;
            List<byte[]> coming = new List<byte[]>();

            sockets[number].Receive(length,4,0);

            len = BitConverter.ToInt32(length, 0);
            remainingBytes = len;

            bufferSize = len < bufferSize ? len : bufferSize;

            while(receivedBytes < len)
            {
                if (remainingBytes > bufferSize)
                {
                    coming.Add(new byte[bufferSize]);
                    //imgBuf = new byte[bufferSize];
                    sockets[number].Receive(coming[current], bufferSize, 0);
                }
                else
                {
                    coming.Add(new byte[remainingBytes]);
                    //imgBuf = new byte[remainingBytes];
                    sockets[number].Receive(coming[current], remainingBytes, 0);
                    bufferSize = remainingBytes;
                }
                remainingBytes -= bufferSize;
                receivedBytes  = bufferSize;
                current  ;
                //Array.Clear(imgBuf, 0, imgBuf.Length);
            }

            using (var stream = new FileStream(@"C:receivedFile.exe",FileMode.Create))
            {
                using (var binaryWriter = new BinaryWriter(stream))
                {
                    foreach (byte[] buffer in coming)
                    {
                        binaryWriter.Write(buffer);
                    }
                }

            }
        }
        catch (Exception ex)
        { this.setText(ex.Message, textBox2); }
  

Редактировать: Спасибо за помощь, у меня все получилось 🙂

         try
        {
            int bufferSize = 1024 * 100;
            int len = 0;
            int receivedBytes = 0;
            int remainingBytes = len;
            int reached = 0;

            byte[] length = new byte[4];
            byte[] imgBuf = new byte[bufferSize];

            int current = 0;
            sockets[number].Receive(length,4,0);

            len = BitConverter.ToInt32(length, 0);
            remainingBytes = len;

            bufferSize = len < bufferSize ? len : bufferSize;
            imgBuf = new byte[len];

            while (reached < len)
            {
                reached  = sockets[number].Receive(imgBuf, receivedBytes, remainingBytes, 0);
                remainingBytes = len - reached;
                receivedBytes = reached;
                current  ;
                //Array.Clear(imgBuf, 0, imgBuf.Length);
            }

            using (var stream = new FileStream(@"C:putty.exe",FileMode.Create))
            {
                using (var binaryWriter = new BinaryWriter(stream))
                {
                        binaryWriter.Write(imgBuf);
                }
            }
            Array.Clear(imgBuf, 0, imgBuf.Length);
        }
        catch (Exception ex)
        { this.setText(ex.Message, textBox2); }
  

Комментарии:

1. Если вы передаете весь файл из файловой системы, вам, вероятно, следует использовать TransmitFile() (если в Windows) или sendfile() (если в Unix)

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

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

4. попробовал и не повезло! все та же проблема, exe-файлы повреждены, а txt и другие типы — нет!

Ответ №1:

Вы не проверяете, сколько байтов вы фактически получили sockets[number].Receive(coming[current], bufferSize, 0); . Он не должен быть равен размеру буфера, который вы объявили. Кроме того, как сказано в комментариях, хранение всего файла в памяти не является хорошей идеей.

Комментарии:

1. я не знаю, насколько это было бы полезно, но это всегда получает ту же длину, что и «bufferSize» . Я отредактировал эту строку на BytesReceived = sockets[число] . Получение (поступление [текущее], размер буфера, 0);

2. Это сложно получить в режиме отладки, просто добавьте исключение для байтов, полученных!=coming[current] . Длина и запуск вашего кода

3. Наконец, я выяснил, что было не так, мне пришлось использовать функцию перегрузки o receive bytess = sockets[number] . Получение (imgBuf, receivedBytes , bufferSize, 0); вместо того, чтобы создавать список байтов и добавлять новый массив байтов при каждом получении, я создал один массив байтов с длиной сообщения и добавил к нему все запросы

4. @Adel Ali Полезно не только проверять полученную длину и обрабатывать только количество байтов, возвращаемых Receive() (в отличие от предположения, что весь буфер заполнен). Это абсолютно необходимо. Возможно, вы не можете воспроизвести случай в своей тестовой среде с заданным вводом / выводом, где это происходит, но когда-нибудь это произойдет.

Ответ №2:

В дополнение к проверке количества полученных байтов, вам необходимо проверить количество байтов, отправленных каждым вызовом send — если ваше окно передачи TCP заполняется, вызов send может отправить меньше данных, чем вы запросили, и в этом случае вам нужно будет повторно отправить неотправленные данные.

В общем, вам ВСЕГДА нужно проверять возвращаемое значение ваших системных вызовов, чтобы проверить все возможные случаи нечетных углов. Прочитайте справочные страницы для send(2) и recv (2) для получения полного списка всего, что может произойти.