1. 程式人生 > >電子郵件傳送的原理以及簡易實現

電子郵件傳送的原理以及簡易實現

    在程式碼開始之前,我們先手工模擬一遍傳送電子郵件的過程,那麼接下來的程式碼你就容易懂多了!

    下面以163郵箱為例!

    

     先鍵入telnet命令,然後連線到網易的smtp伺服器,使用25號埠.

     

     接著看下圖:

<<<<<<

              從現在開始,我們可以模擬傳送信件的過程了!

     傳送命令:EHLO hello  (hello可以任意替換)

     

     然後我們請求登陸!

     傳送命令: AUTH LOGIN

     

     然後傳送自己賬戶的使用者名稱的base64編碼(不要@XXX.com)過去!我用的一個測試郵箱

[email protected],it_is_just_a_test的base64編碼是aXRfaXNfanVzdF9hX3Rlc3Q=

     

      然後將自己的密碼的base64編碼發過去!我的密碼的base64編碼是MTk5MzA3MTRseWg=

     

     伺服器告訴你認證成功了!我們現在就可以傳送郵件了!

     先發送郵件說明:MAIL FROM: <[email protected]>

     然後傳送目的郵箱說明:RCPT TO: <[email protected]>

     然後請求傳送郵件:DATA

        然後傳送郵件頭和郵件體,傳送資料:

     From: [email protected]

     To: [email protected]

     Subject: 測試一下

     MIME-Version: 1.0

     這是一封測試郵件!!! .

     (在正文輸入結束時回車輸入一個 . (英文輸入法下的句號)然後再回車,表示正文部分的結束。這時將顯示郵件成功傳送的資訊。)<CR><LF>表示的是回車

               

                當信件傳送完成了之後,我們可以利用quit命令關閉與伺服器的連線!

                基本原理就是這樣了:然後我們來看程式碼吧!(利用程式傳送的時候無非就是把回車換成"\r\n"!)

#include <iostream>
#include <WinSock2.h>
#include <windows.h>
#include <string.h>
using namespace std;
#pragma  comment(lib, "ws2_32.lib")	/*連結ws2_32.lib動態連結庫*/
const int MAXSIZE = 1024;

int srvPort = 25;
char srvDomain[256] = "smtp.163.com";
char userName[256] = "[email protected]"; //自己的郵箱名稱
char password[256] = "19930714lyh"; //自己郵箱的密碼
char targetEmail[256] = "[email protected]"; //要傳送的郵件地址
char emailTitle[256] = "你好!我是李樹花開!"; //郵件主題
char content[256] = "這是一封測試郵件!很高興我的第一個郵件客戶端製作成功了!";//郵件正文

SOCKET CreateConn(char* pWebsite,int port)
{
	//為建立socket物件做準備,初始化環境
	SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);     //建立socket物件
	SOCKADDR_IN addrSrv;   
	HOSTENT* pHostent;
    pHostent = gethostbyname(pWebsite);  //得到有關於域名的資訊
    
	addrSrv.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]);	//得到smtp伺服器的網路位元組序的ip地址   
	addrSrv.sin_family=AF_INET;   
	addrSrv.sin_port=htons(port);   
	int tf = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));   //向伺服器傳送請求  
	if(tf!=0)
	{
		return 0;
		//printf("連結失敗\n");
	}
	return sockClient;
}

//int DoWhat是否接受資料
void SendAndRecvMsg(
					SOCKET sockClient,		//客戶端的套接字
					char* pMessage,			//要傳送的訊息
					int Messagelen,			//訊息的長度
					int DoWhat,				//造作的型別
					char* recvBuf,			//接收的緩衝區
					int recvBufLen			//緩衝區長度
					)
{
	char lpMessage[256] = {0};
	memcpy(lpMessage, pMessage, Messagelen);
	printf("\n\n%s \n", lpMessage);
	if (DoWhat == 0)
	{
		send(sockClient, lpMessage, Messagelen, 0);
		memset(recvBuf , 0, recvBufLen);
		DWORD num = recv(sockClient, recvBuf, recvBufLen, 0);     //接收資料
		printf("%s \n", recvBuf); 
		int i = 0;
		while(i != num)
		{
			printf("%02X ", recvBuf[i++]); 
			if((i)%16 == 0)
			{
				printf("\n"); 
			}
		}
		printf("\n");
		
	}
	else if (DoWhat == 1)
	{
		send(sockClient, lpMessage, Messagelen, 0);
		
	}
	else if (DoWhat == 2) //僅僅是接收資料
	{
		memset(recvBuf, 0, recvBufLen);
		DWORD num=recv(sockClient,recvBuf,recvBufLen,0);     //接收資料
		cout << recvBuf << endl;
		int i = 0;
		while(i < num)
		{
			printf("%02X ", (byte)recvBuf[i++]); 
			if((i)%16 == 0)
			{
				printf("\n"); 
			}
		}
		printf("\n");
	}
	
}

int GetStrLen(char* pString) //得到字串的長度
{
	int i = 0;
	while(pString[i++] != 0);
	return i-1;
}


void StringToBase64(const char *src,char *dst)
{/*將字串變為base64編碼*/
    int i = 0;
    char *p = dst;
    int d= strlen(src) - 3;
	static const char Base64[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    //for(i=0;i<strlen(src)-3;i+=3) ;if (strlen(src)-3)<0 there is a buf
	
    for(i=0;i<=d;i+=3)
    {
        *p++ = Base64[((*(src + i)) >> 2) & 0x3f];
        *p++ = Base64[(((*(src + i)) & 0x3) << 4) + ((*(src + i + 1)) >> 4)];
        *p++ = Base64[((*(src + i + 1) & 0xf) << 2) + ((*(src + i + 2)) >> 6)];
        *p++ = Base64[(*(src + i + 2)) & 0x3f];
    }
    if((strlen(src) - i) == 1)
    {
        *p++ = Base64[((*(src + i)) >> 2) & 0x3f];
        *p++ = Base64[((*(src + i)) & 0x3) << 4];
        *p++ = '=';
        *p++ = '=';
    }
    if((strlen(src) - i) == 2)
    {
        *p++ = Base64[((*(src + i)) >> 2) & 0x3f];
        *p++ = Base64[(((*(src + i)) & 0x3) << 4) + ((*(src + i + 1)) >> 4)];
        *p++ = Base64[((*(src + i + 1) & 0xf) << 2)];
        *p++ = '=';
    }
    *p = '\0';
}


bool FormatEmail(char* pFrom, char* pTo, char* pSubject, char* pMessage, char* Email)
{/*格式化要傳送的內容*/
	lstrcat(Email, "From: ");
	lstrcat(Email, pFrom);
	lstrcat(Email, "\r\n");
	
	lstrcat(Email, "To: ");
	lstrcat(Email, pTo);
	lstrcat(Email, "\r\n");
	
	lstrcat(Email, "Subject: ");
	lstrcat(Email, pSubject);
	lstrcat(Email, "\r\n");
	
	lstrcat(Email, "MIME-Version: 1.0");
	lstrcat(Email, "\r\n");
	lstrcat(Email, "\r\n");
	
	lstrcat(Email, pMessage);
	
	lstrcat(Email, "\r\n.\r\n");
	return TRUE;
}

void main()
{
	
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	
	wVersionRequested = MAKEWORD(2, 1);
	err = WSAStartup(wVersionRequested, &wsaData);
	SOCKET sockClient = CreateConn(srvDomain, srvPort); //網易smtp郵箱,25號埠

	char buff[MAXSIZE];
    memset(buff, 0, sizeof(char) * MAXSIZE);  //清零

	SendAndRecvMsg(sockClient, 0, 0, 2, buff, MAXSIZE);  //接收資料
	
	char UserNameToSendEmail[256] = {0};
	sprintf(UserNameToSendEmail, "EHLO %s", "[email protected]");
	lstrcat(UserNameToSendEmail, "\r\n\0");
	SendAndRecvMsg(sockClient, UserNameToSendEmail, GetStrLen(UserNameToSendEmail), 0, buff, MAXSIZE); //既接收也傳送

    SendAndRecvMsg(sockClient, "AUTH LOGIN\r\n", strlen("AUTH LOGIN\r\n"), 0, buff, MAXSIZE); //請求登陸
    char pUerName[256] = {0};
	//strstr函式搜尋一個字串在另一個字串中的第一次出現,並返回第一次出現位置的指標

	DWORD p = strstr(userName,"@") - userName; 
	memcpy(pUerName, userName, p); //得到使用者名稱,如從"[email protected]"得到"13203200199"
    char base[256];
    StringToBase64(pUerName, base); //得到使用者名稱的base64編碼

	char str[MAXSIZE];
	memset(str, 0, MAXSIZE);
	sprintf(str, "%s\r\n", base/*"MTMyMDMyMDAxOTk="*/);
	SendAndRecvMsg(sockClient, str, lstrlen(str), 0, buff, MAXSIZE); //傳送使用者名稱,並接收伺服器的返回

	StringToBase64(password, base);
	memset(str, 0, 1024);
	sprintf(str, "%s\r\n", base);
	SendAndRecvMsg(sockClient, str, lstrlen(str),0, buff, MAXSIZE); //傳送使用者密碼,並接收伺服器的返回

	char MailFrom[256] = {0};
	sprintf(MailFrom, "MAIL FROM: <%s>\r\n", userName);
	
	SendAndRecvMsg(sockClient, MailFrom, lstrlen(MailFrom), 0, buff, MAXSIZE);
	
	char RcptTo[256] = {0};
	sprintf(RcptTo, "RCPT TO: <%s>\r\n", targetEmail);
	SendAndRecvMsg(sockClient, RcptTo, lstrlen(RcptTo), 0, buff, MAXSIZE);

	SendAndRecvMsg(sockClient, "DATA\r\n", lstrlen("DATA\r\n"), 0, buff, MAXSIZE);

	char Email[1024] = {0};
	FormatEmail(userName, targetEmail, emailTitle, content, Email);
	
	SendAndRecvMsg(sockClient,Email,lstrlen(Email), 0, buff, MAXSIZE);
	
	SendAndRecvMsg(sockClient,"QUIT\r\n", lstrlen("QUIT\r\n"), 0, buff, MAXSIZE);

    closesocket(sockClient);   
	WSACleanup();
}