C語言使用SMTP發送郵件

1 原理

  1. SMTP
    Simple Mail Transfer Protocol,簡單郵件傳輸協議。簡單發送郵件用到的基本命令如下表,注意每條命令以\r\n結尾。

    命令 格式 說明
    EHLO EHLO 發件人信息 用於向服務器標明用戶身份,可以爲發件人服務器域名或者計算機名,例如EHLO YANG-PC,隨意填寫也能發送成功
    AUTH LOGIN AUTH LOGIN 郵箱認證,發送該命令後依次發送郵箱賬號和密碼(賬號密碼均使用Base64編碼)
    MAIL FROM MAIL FROM:發件郵箱 發件人郵件地址
    RCPT TO RCPT TO:收件郵箱 收件人郵件地址
    DATA DATA 準備開始發送郵件內容,發送該命令後發送郵件內容,注意郵件內容要按下文介紹的郵件數據格式
    QUIT QUIT 結束,返回250表示此次發送成功

    注意:每條命令以\r\n結尾。

    郵件數據格式包含郵件頭和郵件體,格式如下(注意\r\n已經表示換行,以下示例寫成多行只是爲了格式清晰)

    From: "要顯示的發件人名稱"<發件人郵箱>\r\n
    To: "要顯示的收件人名稱"<收件人郵箱>\r\n
    Subject: 郵件主題\r\n\r\n
    郵件內容\r\n.\r\n
  2. Base64編碼
    郵箱賬號和密碼採用Base64編碼,Base64編碼將連續的三個字符按一定規則轉換爲4個字符,達到不能直接看出文本內容的效果。具體規則如下

    • 3個字符以二進制形式連接起來一共24位,然後平均分成4部分得到4個6位的數,查編碼表可得四個字符,這4個字符便是轉換結果
    • 轉換前字符總數可能不是3的倍數,若剩餘一個字符,則將該字符二進制形式後面加4個0,得到兩個6位二進制數,查得兩個字符,然後再加兩個“=”
    • 若剩餘兩個字符,則將兩個字符二進制形式連接起來並在後面加2個0,然後平均分成3部分得到3個6位二進制數,查得3個字符,然後再加一個“=”

    示例

    原字符串 ASCII碼 二進制 二進制重組 編碼索引 編碼結果
    “ABC” 0x41 0x42 0x43 01000001 01000010 01000011 010000 010100 001001 000011 16 20 09 03 “QUJD”
    “+-*/” 0x2B 0x2D 0x2A 0x2F 00101011 00101101 00101010 00101111 001010 110010 110100 101010 001011 110000 10 50 52 42 11 48 “Ky0qLw==”
    “20158” 0x32 0x30 0x31 0x35 0x38 00110010 00110000 00110001 00110101 00111000 001100 100011 000000 110001 001101 010011 100000 12 35 00 49 13 19 32 “MjAxNTg=”

    Base64編碼表

    0 1 2 3 4 5 6 7 8 9 10 11 12
    A B C D E F G H I J K L M
    13 14 15 16 17 18 19 20 21 22 23 24 25
    N O P Q R S T U V W X Y Z
    26 27 28 29 30 31 32 33 34 35 36 37 38
    a b c d e f g h i j k l m
    39 40 41 42 43 44 45 46 47 48 49 50 51
    n o p q r s t u v w x y z
    52 53 54 55 56 57 58 59 60 61 62 63
    0 1 2 3 4 5 6 7 8 9 + /


  3. 支持SMTP的郵箱

    郵箱 SMTP服務器 端口 說明
    126郵箱 smtp.126.com 25 現在需要開通客戶端授權密碼等認證才能開通SMTP服務,不過我N年前註冊的直接就開通的
    163郵箱 smtp.163.com 25 應該和126一樣的
    sina郵箱 smtp.sina.com 25 開啓SMTP服務需要綁定手機

2 實現

一個完整示例

#include <windows.h>
#include <stdio.h>
#include <WinSock.h>
#pragma comment(lib, "ws2_32.lib")

#define SMTP_BUFSIZE 1024

void EncodeBase64(char* src, char* encode);                             // Base64編碼
int SendMail(char *from, char *pwd, char* to, char* title, char* text); // 發送郵件

int main()
{
    WSADATA WSAData;
    WSAStartup(MAKEWORD(2, 2), &WSAData);
    SendMail("xxxxxxxx@126.com", "xxxxxxxx", "xxxxxxxx@hotmail.com", "Hello", "This is a test mail");
    WSACleanup();
    return 0;
}

// Base64編碼
void EncodeBase64(char* src, char* encode)
{
    char base64_table[] = {
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
        'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
        'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
    int len = strlen(src);
    int i = 0;
    for(i = 0; i < len/3; i++)
    {
        int temp = byte(src[3*i+2]) + (byte(src[3*i+1]) << 8) + (byte(src[3*i]) << 16);
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[(temp & 0xfc0) >> 6];
        encode[4*i+3] = base64_table[temp & 0x3f];
    }
    encode[4*i] = 0;
    if (1 == len % 3)
    {
        int temp = byte(src[3*i]) << 16;
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[64];
        encode[4*i+3] = base64_table[64];
        encode[4*i+4] = 0;
    }
    else if (2 == len % 3)
    {
        int temp =(byte(src[3*i+1]) << 8) + (byte(src[3*i]) << 16);
        encode[4*i] = base64_table[(temp & 0xfc0000) >> 18];
        encode[4*i+1] = base64_table[(temp & 0x3f000) >> 12];
        encode[4*i+2] = base64_table[(temp & 0xfc0) >> 6];
        encode[4*i+3] = base64_table[64];
        encode[4*i+4] = 0;
    }
}

// 發送郵件
int SendMail(char *from, char *pwd, char* to, char* title, char* text)
{
    char buf[SMTP_BUFSIZE] = {0};
    char account[128] = {0};
    char password[128] = {0};
    // 連接郵件服務器
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(25);
    hostent* phost = gethostbyname("smtp.126.com");
    memcpy(&addr.sin_addr.S_un.S_addr, phost->h_addr_list[0], phost->h_length);
    SOCKET sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("smtp socket() error");
        return 1;
    }
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr)) < 0)
    {
        printf("smtp connect() error");
        return 2;
    }
    // EHLO
    char pcname[128] = {0};
    DWORD size = 128;
    GetComputerName(pcname, &size); // 獲取計算機名
    sprintf_s(buf, SMTP_BUFSIZE, "EHLO %s\r\n", pcname);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // AUTH LOGIN
    sprintf_s(buf, SMTP_BUFSIZE, "AUTH LOGIN\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 郵箱賬號
    EncodeBase64(from, account);
    sprintf_s(buf, SMTP_BUFSIZE, "%s\r\n", account);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 密碼
    EncodeBase64(pwd, password);
    sprintf_s(buf, SMTP_BUFSIZE, "%s\r\n", password);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // MAIL FROM 發件人
    sprintf_s(buf, SMTP_BUFSIZE, "MAIL FROM:<%s>\r\n", from);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // RCPT TO 收件人
    sprintf_s(buf, SMTP_BUFSIZE, "RCPT TO:<%s>\r\n", to);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // DATA 準備開始發送郵件內容
    sprintf_s(buf, SMTP_BUFSIZE, "DATA\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // 發送郵件內容
    sprintf_s(buf, SMTP_BUFSIZE, 
        "From: \"yang\"<%s>\r\nTo: \"test\"<%s>\r\nSubject: %s\r\n\r\n%s\r\n.\r\n", from, to, title, text);
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    // QUIT 結束
    sprintf_s(buf, SMTP_BUFSIZE, "QUIT\r\n");
    send(sockfd, buf, strlen(buf), 0);
    recv(sockfd, buf, SMTP_BUFSIZE, 0);
    if (strlen(buf) >= 3)
    {
        if (buf[0] == '2' && buf[1] == '5' && buf[2] == '0')
        {
            printf("sucess\n");
        }
    }
    closesocket(sockfd);
    system("pause");
    return 0;
}

本文原文鏈接 http://blog.csdn.net/yanglx2022/article/details/47759069

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章