#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.