Когда файл отправляется через SMTP, он теряет несколько байтов. c

#c #smtp #file-transfer #windows-socket-api

#c #smtp #передача файлов #windows-socket-api

Вопрос:

я пытаюсь отправить электронное письмо через SMTP, я отправляю составную почту с текстовой частью и частью application / octet-stream. когда я пытаюсь отправить файлы «не * .txt», например .jpg или .docx, он поврежден, и некоторые байты теряются. Например, когда я пытаюсь отправить файл 123.docx , размер этого файла составляет 166 020 байт. я получаю файл по электронной почте, но в нем всего 166 006, и я не могу его открыть. Переменная «total» показывает правильное количество отправленных байтов. Мой код приведен ниже:

 #include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
using namespace std;


int fileSize(char fileName[])
{
    std::streampos fsize = 0;

    std::ifstream myfile (fileName, ios::in);  // File is of type const char*

    fsize = myfile.tellg();         // The file pointer is currently at the beginning
    myfile.seekg(0, ios::end);      // Place the file pointer at the end of file

    fsize = myfile.tellg() - fsize;
    myfile.close();

    cout << "size is: " << fsize << " bytes.n";
    return fsize;
}

int main() {
char text[2048];
//connecting to smtp server

    //sending DATA
    strcpy(text,"DATAn");
    send(s,text,strlen(text),0);

    recv(s,text,sizeof(text),0);
    cout<<"recv - "<<text<<endl;

    //FROM field
    strcpy(text,"FROM: laboratory4_mm@rambler.run");
    send(s,text,strlen(text),0);
    
    //TO field
    strcpy(text,"TO: ");
    strcat(text,reciever);
    strcat(text,"n");
    send(s,text,strlen(text),0);

    // SUBJECT field 
    char subject[2048];
    cout<<"Enter the theme of the letter"<<endl;
    cin.getline(subject,2048);
    strcpy(text,"SUBJECT: ");
    strcat(text,subject);
    strcat(text,"n");
    send(s,text,strlen(text),0);

    // delimeter of multipart message
    strcpy(text,"Content-Type: multipart/mixed; boundary="---nsabnqeaSA43ds2"n");
    send(s,text,strlen(text),0);

    //Text part
    strcpy(text,"-----nsabnqeaSA43ds2nContent-Type: text/plain; charset=utf8nContent-Transfer-Encoding: 8bitnn");
    send(s,text,strlen(text),0);

    cout<<"Enter the text:"<<endl;
    cin.getline(text,2048);
    send(s,text,strlen(text),0);

    //File part
    char fileName[256];
    cout<<"Enter file name: ";
    cin.getline(fileName,255);
    int size = fileSize(fileName);
    char fileLength[1024];
    itoa(size,fileLength,10);
    cout<<fileLength<<endl;
    strcpy(text,"n-----nsabnqeaSA43ds2 nContent-Type: application/octet-streamnContent-Length: ");
    strcat(text,fileLength);
    strcat(text,"nContent-Transfer-Encoding: binarynContent-Disposition: attachment;nn");
    send(s,text,strlen(text),0);
    
    ifstream fin;
    fin.open(fileName,ios::binary);
    char *buf = new char[1024];
    int readBytes;
    int total =0;
    while((readBytes = fin.read(buf,1024).gcount())>0) {
         int sent= send(s,buf,readBytes,0);
         total =sent;
         delete buf;
         buf = new char[1024];
    }
    fin.close();
    delete buf;
    cout<< total<<endl;

    strcpy(text,"n-----nsabnqeaSA43ds2--n");
    send(s,text,strlen(text),0);

// telling that DATA is over
    strcpy(text,"n.n");
    send(s,text,strlen(text),0);
    fout<<text;
    recv(s,text,sizeof(text),0);
    cout<<"recv - "<<text<<endl;

//Disconnecting from server
return 0;
}

  

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

1. Вам нужно проверить возвращаемые значения и errno, чтобы получить лучшее представление о том, что происходит.

2. Я добавил эту строку, чтобы проверить, отправлены ли не все байты if(sent!=readBytes) cout<<"ERROR"<<endl; . Также я создал выходной поток для копирования данных из входного файла, и с этой копией все в порядке, в ней есть все байты.

3. Разве вы не должны кодировать двоичные данные base64, отправляемые через MIME?

4. Если я скажу «Content-Transfer-Encoding: binary», мне все равно придется кодировать файл?

5. Ваша fileSize функция открывает файл в текстовом режиме, а не в двоичном. Таким образом, пары CRLF будут неправильно подсчитаны как один символ.

Ответ №1:

Здесь есть несколько фундаментальных ошибок в кодировке MIME:

Во-первых, предполагается, что пустая строка отделяет заголовки от содержимого. Пустая строка отсутствует:

 strcpy(text,"Content-Type: multipart/mixed; boundary="---nsabnqeaSA43ds2"n");
  

Здесь сгенерирована одна новая строка.

 strcpy(text,"-----nsabnqeaSA43ds2nContent-Type: text/plain; charset=utf8nContent-Transfer-Encoding: 8bitnn");
  

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

Кроме того, новая строка, которая предшествует разделителю границ, является логической частью разделителя границ, поэтому новая строка также отсутствует. Дополнительную информацию см. в документации MIME.

 strcpy(text,"n-----nsabnqeaSA43ds2 nContent-Type: application/octet-streamnContent-Length: ");
  

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

Во-вторых:

 while((readBytes = fin.read(buf,1024).gcount())>0) {
     int sent= send(s,buf,readBytes,0);
  

Отправка содержимого двоичного файла, как есть? Это не сработает. Несмотря на то, что это может быть или не быть правильной MIME-кодировкой, SMTP по-прежнему является простым текстовым протоколом передачи. Существует расширение для передачи двоичных данных, но показанный код его не использует.

Как есть, это версия SMTP с неопределенным поведением. Нет гарантированных результатов. Если вам нужно надежно прикрепить этот файл, вы должны закодировать его в base64.