您现在的位置是:首页 >技术教程 >SMTP简单邮件传输协议(C/C++ 发送电子邮件)网站首页技术教程

SMTP简单邮件传输协议(C/C++ 发送电子邮件)

程序猿编码 2024-08-10 00:01:02
简介SMTP简单邮件传输协议(C/C++ 发送电子邮件)

SMTP是用于通过Internet发送电子邮件的协议。电子邮件客户端(如Microsoft Outlook或macOS Mail应用程序)使用SMTP连接到邮件服务器并发送电子邮件。邮件服务器还使用SMTP将邮件从一个邮件服务器交换到另一个。它不用于从服务器下载电子邮件;相反,IMAP和POP3协议检索邮件。

SMTP客户端首先通过TCP与邮件服务器建立连接。然后,客户端将消息推送到服务器,服务器会检查收件人的电子邮件地址,看看他们是否与发件人在同一个域上;如果他们是,它可以传递信息。否则,它会运行DNS查询来查找收件人邮件服务器的IP地址。然后,邮件服务器与目标邮件服务器建立另一个SMTP连接以发送邮件。

SMTP的基本命令

正如我们前面提到的,SMTP命令是一组代码,为服务器之间的电子邮件传输提供动力。以下是您应该注意的基本SMTP命令:

HELO或EHLO(你好):这是开始整个电子邮件发送过程的关键命令。电子邮件客户端正在向SMTP服务器标识自己。这是一个对话的开始,通常涉及服务器发回一个HELO命令及其域名/IP地址。

MAIL FROM:根据标识命令,发件人将共享指定邮件发件人的代码。这会列出电子邮件地址,并告诉SMTP服务器新事务即将开始。从这里开始,服务器重置所有内容,并准备接受电子邮件地址。一旦被接受,它将回复一个250 OK的回复代码。

RCPT TO(Recipient TO):下一个命令位于250 OK回复代码之后,该代码用于识别电子邮件的发送对象。同样,SMTP服务器使用相同的代码进行响应,此时可以使用不同收件人的电子邮件地址发送另一个RCPT TO命令。这可以根据需要来回多次,具体取决于收到电子邮件的人数。

DATA 数据:这会触发客户端和服务器之间的数据传输。所有邮件内容都将移动到SMTP服务器,SMTP服务器将以345回复代码进行响应。消息的内容被传输到服务器,在服务器中,一个点被单独发送到一行中,以表示消息的结束。如果接受并准备好交付,服务器将发送另一个250 OK代码。此时,邮件正在发送给收件人的途中。

QUIT:发送电子邮件后,客户端向服务器发送QUIT命令,切断连接。如果已成功关闭,服务器将回复221代码。

RSET(Reset):当邮件事务需要中止时,会将此命令发送到服务器。它不会关闭连接,但会重置所有内容,并删除有关电子邮件和相关方的所有以前的数据。当出现错误时,通常会使用此方法,例如输入错误的收件人信息,并且需要重新启动流程。

将这些命令想象成允许电子邮件服务器之间进行对话的语言。他们的聊天看起来有点像这样:


还有其他SMTP命令可以处理身份验证并增强安全性,例如AUTH和STARTTLS。如果您有兴趣了解它们或查看SMTP在工作中的示例,请阅读本SMTP命令指南。

Linux使用mail配合smtp发送邮件

邮件安装工具:

yum -y install mailx
yum -y install sendmail

开启与关闭邮件服务:

systemctl enable sendmail
systemctl stop sendmail

开启邮箱imap/smtp服务

我这里使用的是163的邮箱,步骤如下:
登录邮箱后,点击页面顶部的“设置”菜单,在下拉框中点击“POP3/SMTP/IMAP”项

由于我这里已经开启过,就不再演示了。初始勾选后会让你填写手机号码,发送验证码,成功填写后,会让你输入授权码,这个是作为smtp登录的密码使用的,详情请看163的smtp说明

配置发送的邮箱和密码,注意不是你的邮箱登录密码。

# vi /etc/mail.rc

在底部添加:
set from="xxx@163.com"
set smtp=smtp.163.com
set smtp-auth-user=xxx@163.com
set smtp-auth-password=自己填写的授权码
set smtp-auth=login

测试发送

echo -e “This is a fine test body…” | mail -s “TestMail” xxx@163.com

为帮助您熟悉使用 SMTP 协议发送邮件的流程,提供以下 telnet 命令会话过程示例,来描述 SMTP 命令会话过程。

备注:其中 S 代表服务器,C 代表客户端。可以使用 Linux 命令 echo -n Content|base64 进行 base64 编码。

$telnet smtpdm.aliyun.com 25
S:220 163.com Anti-spam GT for Coremail System (163com[20141201])
C:EHLO test.com
S:250 OK
C:AUTH LOGIN
S:334 dXNlcm5hbWU6
C:YSoqKkBleGFtcGxlLm5ldA==         备注:用户名a***@example.net的base64编码
S:334 UGFzc3dvcmQ6
C:dGVzdA==                     备注:用户密码test的base64编码
S:235 Authentication successful
C:MAIL FROM: <a***@example.net>   备注:注意用 <> 将发件人扩起来
S:250 Mail Ok
C:RCPT TO: <a***@example.net>
S:250 Rcpt Ok
C:DATA
S:354 End data with <CR><LF>.<CR><LF>
C:subject: test
C:from: <a***@example.net>
C:to: <a***@example.net>
C:
C:test
C:.
S:Data Ok: queued as freedom ###envid=148316944
C:QUIT
S:221 Bye

了解SMTP错误代码

电子邮件发送过程并不总是像上面我们的电子邮件服务器聊天的例子那样顺利。反弹、阻止或其他问题可能会阻止发送电子邮件。在这种情况下,接收服务器可以使用SMTP错误代码通知您问题,了解它们的含义可以帮助您诊断和修复电子邮件传递障碍。

例如,以下是两组经常出现的SMTP错误:

4.X.X持续瞬态故障:这些错误代码以数字“4”开头,后面跟着另外两个数字。它们通常意味着邮件服务器出现临时故障。再次重复该命令可以消除错误,但服务器经常使用这些代码来阻止不受信任的发件人。

5.X.X永久错误:这些错误代码以数字“5”开头,后面跟着两个数字。它们通常表示SMTP连接已断开。如果你试图重新发送电子邮件,很可能仍然会导致同样的错误。

#define SMTP_SERV_NOSERVICE 421 /* Server error: <domain> Service not available, closing transmission channel */
#define SMTP_SERV_NOMAILBOX 450 /* Server error: Requested mail action not taken: mailbox unavailable */
#define SMTP_SERV_ACTABORT	451 /* Server error: Requested action aborted: local error in processing */
#define SMTP_SERV_NOSTORAGE 452 /* Server error: Requested action not taken: insufficient system storage */
#define SMTP_SERV_NOTLS		454 /* Server error: TLS not available */
#define SMTP_SERV_SYNTAX	500 /* Syntax error, command unrecognised */
#define SMTP_SERV_SYNTAX_P	501 /* Server error: Syntax error in parameters or arguments*/
#define SMTP_SERV_UNKNOWN	502 /* Server error: Unknown command */
#define SMTP_SERV_BADSEQ	503 /* Server error: Bad sequence of commands */
#define SMTP_SERV_NCMDPAR	504 /* Server error: Command parameter not implemented */
#define SMTP_SERV_NOACCMAIL	521 /* Server error: <domain> does not accept mail (see rfc1846) */
#define SMTP_SERV_STARTTLS  530 /* Server error: Start TLS needed */
#define SMTP_SERV_AUTHERR	535 /* Server error: Authentication failed */
#define SMTP_SERV_NOMBOXNM	550 /* Server error: Requested action not taken: mailbox unavailable */
#define SMTP_SERV_USRNOLOCL 551 /* Server error: User not local; please try <forward-path> */
#define SMTP_SERV_EXCSTOR	552 /* Server error: Requested mail action aborted: exceeded storage allocation */
#define SMTP_SERV_MBXNMNALL 553 /* Server error: Requested action not taken: mailbox name not allowed */
#define SMTP_SERV_TMUNKCOM  554 /* Server error: Transaction failed*/

SMTP与其他电子邮件协议有何不同?

回想一下SMTP的定义,你会记得我们说过它是许多电子邮件协议之一。POP和IMAP是另外两种最常用的电子邮件协议。

这些协议之间的主要区别在于,SMTP是将电子邮件从一个未知的邮件服务器发送或“推送”到另一个未知邮件服务器的唯一协议。POP和IMAP是用于从自己的邮件服务器接收或“拉取”收件人邮件的协议。因此,POP和IMAP仅限制将邮件传输到经过验证的邮件服务器。它们不能用于您自己网络之外的通信。

发送过程中的不同协议:SMTP用于发送电子邮件,POP和IMAP用于接收邮件

下面,我们将更深入地解释POP和IMAP的工作原理以及它们与SMTP的区别。

POP

POP代表邮局协议,用于接收传入消息。最新的版本是POP3,上次更新是在1988年。

这个协议之所以得名,是因为它的运作方式就像数字领域中现实生活中的邮局。POP3将接收电子邮件,并为客户保留这些电子邮件,直到他们收到为止。所有电子邮件都下载并存储在本地,这对于只使用一台计算机查看电子邮件的人来说是一个方便的解决方案。它也被企业普遍使用,因此员工可以在离线时查看电子邮件。

POP和SMTP有何不同?

SMTP是一种消息传输协议,而POP是一种邮件访问协议。换句话说,SMTP用于将邮件从一个用户发送到另一个用户,而POP用于接收电子邮件。

SMTP使用两次:一次是在发件人和电子邮件服务器之间建立连接并发送信息时,第二次是在发送信息并连接到收件人时。POP在接收者和他们的邮件服务器之间只使用一次。

IMAP

IMAP代表Internet邮件访问协议。简而言之,IMAP将邮件存储在电子邮件服务器上,但用户可以访问该服务器来检查和配置他们的电子邮件。这与POP的区别在于IMAP使用云服务器,因此任何设备都可以对电子邮件进行身份验证和分类。许多电子邮件用户更喜欢IMAP而不是POP,因为它既方便又高效。

IMAP和SMTP有何不同?

SMTP是一种消息传输协议,而IMAP是一种邮件访问协议(与POP类似)。因此,当SMTP发送邮件并处理传出的电子邮件时,IMAP只检索邮件并处理传入的电子邮件。

SMTP简单邮件传输协议(C/C++ 发送电子邮件)


int smtp_init(SMTP_Client **smtp);

int smtp_free(SMTP_Client **smtp);

int smtp_connect(SMTP_Client *smtp, const char *smtpHostName, const unsigned short smtpPort, int security);

int smtp_close(SMTP_Client *smtp);

ssize_t smtp_createLetter(SMTP_Client *smtp,
                          int textFormat,
                          const char *fromName, const char *fromMail,
                          const char *toName,   const char *toMail,
                          const char *mailSubject, const char *mailBody);

ssize_t smtp_attachFile(SMTP_Client *smtp, const char *filePath);

ssize_t smtp_endLetter(SMTP_Client *smtp);

int smtp_login(SMTP_Client *smtp, const char *smtpLogin, const char *smtpPasswd);

int smtp_sendLetter(SMTP_Client *smtp);


static ssize_t smtp_sendData_real(const char *file, int line, SMTP_Client *smtp, const unsigned char *data, size_t size)
{
    if(smtp->debugPrint)
    {
        char *duped = strdup((const char*)data);
        removeEndlEnding(duped);
        fprintf(smtp->debugStream, "[%s][%d] OUT>>: %s
", file, line, duped);
        free(duped);
    }
    if(smtp->p->ssl)
        return smtp_sslSend(smtp, data, size);
    else
        return socketWrite(smtp->p->socketFd, data, size);
}

static ssize_t smtp_recvData_real(const char *file, int line, SMTP_Client *smtp, unsigned char *data, size_t size)
{
    ssize_t ret = 0;
    if(smtp->p->ssl)
        ret = smtp_sslRecv(smtp, data, size);
    else
        ret = socketRead(smtp->p->socketFd, data, size);
    if(smtp->debugPrint)
    {
        char *duped = strdup((const char*)data);
        removeEndlEnding(duped);
        fprintf(smtp->debugStream, "[%s][%d] IN <<: %s
", file, line, duped);
        free(duped);
    }
    return ret;
}

static int recvStatus(SMTP_Client *smtp, const unsigned char *recvString)
{
...
    if(smtp->debugPrint)
        fprintf(smtp->debugStream, "[%s][%d] status = %d
", __FILE__, __LINE__, atoi(statusStr));

    switch(reply)
    {
    case 250: case 235: case 354: case 334: case 221: break;
    default:
        if(smtp->debugPrint)
        {
            fprintf(smtp->debugStream, "Received status is an error!");
        }
        return -1;
    }

    return 0;
}

int main()
{
    ssize_t ret;
    SMTP_Client *smtp;

    int security = SMTP_NONSECURE;
    const char *smtpServer     = SMTP_SAMPLE_HOSTNAME;
    const char *smtpLogin      = SMTP_SAMPLE_LOGIN;
    const char *smtpPassword   = SMTP_SAMPLE_PASSWORD;

    const char *letterSubj     = "Hello :3";
    const char *letterBody     = "this is test!, <b>My Little Vixie!</b>! <span style="font-size: 50px;">:3</size><br>";

    if(smtp_init(&smtp) < 0)
    {
        fprintf(stderr, "Can't initialize SMTP!
");
        return 1;
    }

    /* 要打开调试输出,请设置这些标志 */
    smtp->debugPrint  = 1;
    smtp->debugStream = stderr;

    /* 初始化数据 */
    ret = smtp_createLetter(smtp,
                            SMTP_TextHTML,
                            SMTP_SAMPLE_NAME_FROM, SMTP_SAMPLE_MAIL_FROM,
                            SMTP_SAMPLE_NAME_TO, SMTP_SAMPLE_MAIL_TO,
                            letterSubj, letterBody);
    #if SMTP_SAMPLE_USE_SSL
        security = SMTP_SSL;
    #endif

    if(smtp_connect(smtp, smtpServer, SMTP_SAMPLE_PORT, security) < 0)
    {
        fprintf(stderr, "connect FAILED ... [%s]
", smtp->errorString);
        smtp_free(&smtp);
        return 1;
    }

    printf("connect OK ...
");

    ret = smtp_login(smtp, smtpLogin, smtpPassword);
    if(ret < 0)
    {
        fprintf(stderr, "auth FAILED ... [%s]
", smtp->errorString);
        smtp_free(&smtp);
        return 1;
    }
    printf("auth OK ...
");

    ret = smtp_sendLetter(smtp);
    if(ret < 0)
    {
        fprintf(stderr, "send FAIL ... [%s]
", smtp->errorString);
        smtp_free(&smtp);
        return 1;
    }
    printf("send OK ...
");

    smtp_free(&smtp);

    return 0;
}

运行结果:


If you need the complete source code, please add the WeChat number (c17865354792)

总结

SMTP代表简单邮件传输协议。SMTP是一套交互准则,允许软件通过互联网传输电子邮件,称为简单邮件传输协议。

SMTP的主要目标是用于设置服务器之间的通信规则。服务器有一种识别自己的方式,并宣布他们试图执行什么样的通信。他们还有一种处理错误的方法,例如错误的电子邮件地址。例如,如果收件人地址错误,则会收到带有某种错误消息的服务器回复。

Welcome to follow WeChat official account【程序猿编码

参考:rfc821.txt

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。