首页 > 解决方案 > 当文件通过 SMTP 发送时,它会丢失一些字节。C++

问题描述

我正在尝试通过 SMTP 发送电子邮件,我发送多部分邮件,包含文本部分和应用程序/八位字节流部分。当我尝试发送非 *.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,"DATA\n");
    send(s,text,strlen(text),0);

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

    //FROM field
    strcpy(text,"FROM: laboratory4_mm@rambler.ru\n");
    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,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");
    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-stream\nContent-Length: ");
    strcat(text,fileLength);
    strcat(text,"\nContent-Transfer-Encoding: binary\nContent-Disposition: attachment;\n\n");
    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;
}

标签: c++smtpfile-transferwindows-socket-api

解决方案


MIME 编码中有几个基本错误,这里:

首先,空行应该将标题与内容分开。空行丢失:

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

此处生成一个换行符。

strcpy(text,"-----nsabnqeaSA43ds2\nContent-Type: text/plain; charset=utf8\nContent-Transfer-Encoding: 8bit\n\n");

邮件内容从这里开始,前面没有空行。

此外,边界分隔符之前的换行符是边界分隔符的逻辑部分,因此也缺少换行符。有关详细信息,请参阅 MIME 文档。

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

请注意,此处您在边界分隔符之前显式发送换行符,因此您必须了解此要求。

第二:

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

按原样发送二进制文件的内容?那不会飞的。尽管它可能是正确的 MIME 编码,也可能不是正确的 MIME 编码,但 SMTP 仍然是基于纯文本的传输协议。有一个用于传输二进制数据的扩展,但显示的代码没有使用它。

照原样,这是未定义行为的 SMTP 版本。没有保证的结果。如果您需要可靠地附加此文件,则必须对其进行 base64 编码。


推荐阅读