SMTP 란?
간이 전자우편 전송 프로토콜 (Simple Mail Transfer Protocol, SMTP).
인터넷에서 이메일을 보낼때 쓰는프로토콜. TCP포트번호는 25번이다. 텍스트 기반 프로토콜로서 모든 문자가 7bit ASCII로 되어있어야 한다고 규정되어있다. (단 이번 실습에서 gmail을 쓰는데, gmail은 보안상의 이유로 587번 포트를 사용한다.)
예시
다음은 bob@example.com 가 alice@example.com와 theboss@example.com에게 메일을 보내는 예시.
S는 서버가 보내는 메시지, C: 는 클라이언트가 보내는 메시지다.(우리가 smtp서버의 클라이언트)
S: 220 smtp.example.com ESMTP Postfix
C: HELO relay.example.com
S: 250 smtp.example.com, I am glad to meet you
C: MAIL FROM:<bob@example.com>
S: 250 Ok
C: RCPT TO:<alice@example.com>
S: 250 Ok
C: RCPT TO:<theboss@example.com>
S: 250 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: From: "Bob Example" <bob@example.com>
C: To: Alice Example <alice@example.com>
C: Cc: theboss@example.com
C: Date: Tue, 15 January 2008 16:02:43 -0500
C: Subject: Test message
C:
C: Hello Alice.
C: This is a test message with 5 header fields and 4 lines in the message body.
C: Your friend,
C: Bob
C: .
S: 250 Ok: queued as 12345
C: QUIT
S: 221 Bye
{The server closes the connection}
보면 알겠지만 SMTP는 TCP연결을 성공하면 서버가 먼저 메시지를 보내온다 그런 프로토콜인것. 때문에 connect를 성공했다 하더라도 read/recv로 서버가 보낸 220응답을 확인하고 ehlo를 보내야한다.
사용 준비
예시에서는 TCP연결이후 바로 통신이 이루어지지만, Gmail 의 smtp서버를 실제로 사용해보니 여러가지 인증절차가 추가됐다. 때문에 Gmail의 smtp서버를 이용하려면 이런 준비를 갖춰야한다. 나도 다른 블로그 게시글을 참조해서 했는데 된거라 글 아래 게시글을 참고.
1. gmail 계정 생성
gmail계정이 있어야한다.

2. Google 계정 관리

3. 보안 - 2단계 인증
난이미 해놓은상태라 이미지는 다른데서 가져왔다.

4. 앱 비밀번호 - 앱 전용 비밀번호 생성
나는 이미 mail-sender 라는 이름으로 만들어둔 상태다.


만들면 16자리 비밀번호를 한번만 보여주는데, 어디에 잘 저장해둔다.
Gmail로 smtp 사용하기 with c++
사용환경은 도커 debian:trixie 컨테이너 환경(리눅스 환경).
이제 사용해보자. gmail은 보안 문제로 사실상 더이상 25번 포트로 smtp를 보낼 수 없다. 대신 587포트로 TCP 연결뒤에 TLS연결을 하면 가능하다. 과거에는 465번 포트로 ssl을 적용했다 하는데, 현재는 587번이 권장된다고 한다.
1. 라이브러리 설치 , 빌드옵션
smtp자체는 라이브러리가 필요없지만, 보안 연결을 위한 TLS를 사용하려면 openssl 라이브러리를 설치해야한다.
설치 명령어
apt install -y libssl-dev
컴파일러 라이브러리 링크 옵션
-lssl -lcrypto
자세한 openssl 라이브러리의 사용법은 아래 게시글을 참조하자.
openssl example with.C/C++
도커 debian:trixie 컨테이너환경에서 작성된 글입니다.ai의 도움과 여러 블로그를 참조하며 작성된 게시글입니다. SSL/TLS 그리고 인증서란 무엇인가유튜브 채널 생활코딩의 강의영상을 보고 정리한
dodontak.tistory.com
2. 소스코드
뭐가 긴데 결국 그냥 프로토콜대로 서버가 메시지 주고, 클라가 메시지 보내고를 반복한다.
원래 smtp에서 TLS와 gmail 앱 비밀번호 정책 부분이 추가된 모습이다.
에러에 대한 처리는 전혀 안했다. 실제로 c++로 만든 코드를 쓸거라면 에러처리를 잘 해야할 것.
#include <openssl/ssl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string>
#include <netdb.h>
#include <iostream>
using namespace std;
#define READ_SIZE 2000
string Base64Encode(const string& input);
int main()
{
/*=========================================================
변수 준비
=========================================================*/
string emailFrom = "dodontak2@gmail.com";
string emailTo = "받는사람@example.com";
string DNSAddress = "google.co.kr";
string STMPServer = "smtp.gmail.com";
int serverPort = 587;
string smtpAppPassword = "xxxxxxxxxxxxxxxx";
string subject = "mail from SMTP client!";
string message = "hi! im dodontak! nice to meet you!";
std::string msgId = std::to_string(time(nullptr)) + "-" + emailFrom;
size_t readLen = 0;
size_t writeLen = 0;
char readBuffer[READ_SIZE + 1];
string writeBuffer;
/*=========================================================
TCP 소켓 연결
=========================================================*/
int serverSock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in mailServerAddr;
memset(&mailServerAddr, 0, sizeof(struct sockaddr_in));
hostent* host = gethostbyname(STMPServer.c_str());
memcpy(&(mailServerAddr.sin_addr), host->h_addr_list[0], host->h_length);
mailServerAddr.sin_family = host->h_addrtype;
mailServerAddr.sin_port = htons(serverPort);
connect(serverSock, (sockaddr*)&mailServerAddr, sizeof(mailServerAddr));
/*=========================================================
TLS 준비(연결x)
=========================================================*/
const SSL_METHOD* method = TLS_client_method();
SSL_CTX* ctx = SSL_CTX_new(method);
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, serverSock);
/*=========================================================
ehlo
=========================================================*/
readLen = read(serverSock, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "ehlo " + DNSAddress + "\r\n";
write(serverSock, writeBuffer.data(), writeBuffer.length());
cout << "C: " << writeBuffer;
writeBuffer.clear();
/*=========================================================
START TLS
=========================================================*/
readLen = read(serverSock, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "STARTTLS\r\n";
cout << "C: " << writeBuffer;
write(serverSock, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
TLS ehlo
=========================================================*/
readLen = read(serverSock, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
SSL_connect(ssl);
writeBuffer = "ehlo " + DNSAddress + "\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
AUTH LOGIN
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "AUTH LOGIN\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
encodedEmail
=========================================================*/
string encodedEmail = Base64Encode(emailFrom);
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = encodedEmail + "\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
encodedPw
=========================================================*/
string encodedPw = Base64Encode(smtpAppPassword);
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = encodedPw + "\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
mail
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "mail from:<" + emailFrom + ">\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
rcpt
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "rcpt to:<" + emailTo + ">\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
data
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "data\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
Subject
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "To: " + emailTo + "\r\n" +
"From: " + emailFrom + "\r\n" +
"Subject: " + subject + "\r\n" +
"Message-ID: " + msgId + "\r\n\r\n" +
message + "\r\n.\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
/*=========================================================
quit
=========================================================*/
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
writeBuffer = "quit\r\n";
cout << "C: " << writeBuffer;
SSL_write(ssl, writeBuffer.data(), writeBuffer.length());
writeBuffer.clear();
readLen = SSL_read(ssl, readBuffer, READ_SIZE);
readBuffer[readLen] = 0;
cout << "S: " << readBuffer;
}
string Base64Encode(const string& input)
{
BIO* bio = BIO_new(BIO_f_base64());
BIO* bmem = BIO_new(BIO_s_mem());
bio = BIO_push(bio, bmem);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // 줄바꿈 없이
BIO_write(bio, input.c_str(), (int)input.size());
BIO_flush(bio);
BUF_MEM* bptr;
BIO_get_mem_ptr(bio, &bptr);
string result(bptr->data, bptr->length);
BIO_free_all(bio);
return result;
}
cout으로 출력한 결과
민감할 수 있는 정보는 가려놨다.
S: 220 smtp.gmail.com ESMTP d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
C: ehlo google.co.kr
S: 250-smtp.gmail.com at your service, [민감한 정보]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C: STARTTLS
S: 220 2.0.0 Ready to start TLS
C: ehlo google.co.kr
S: 250-smtp.gmail.com at your service, [민감한 정보]
250-SIZE 35882577
250-8BITMIME
250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
C: AUTH LOGIN
S: 334 민감한 정보
C: 민감한 정보
S: 334 민감한 정보
C: 민감한 정보
S: 235 2.7.0 Accepted
C: mail from:<보내는사람@gmail.com>
S: 250 2.1.0 OK d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
C: rcpt to:<받는사람@gmail.com>
S: 250 2.1.5 OK d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
C: data
S: 354 Go ahead d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
C: To: 받는사람@gmail.com
From: 보내는사람@gmail.com
Subject: mail from SMTP client!
Message-ID: 1773463493-보내는사람@gmail.com
hi! im dodontak! nice to meet you!
.
S: 250 2.0.0 OK 1773463495 d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
C: quit
S: 221 2.0.0 closing connection d9443c01a7336-2aece7ee5b1sm39797005ad.46 - gsmtp
결과물
나는 나한테 보냈다.

잘 된다!
참고한 글 모음
가장 처음 SMTP라는게 있다는걸 알게된 글
c/c++ 메일 전송(smtp프로토콜) | 이론과 실습
🎄 개요 안녕하세요! 오랜만에 c++ 네트워크글이 돌아왔습니다... 이번에는 저가 엄청나게 유익한 내용을 들고왔는데요.. 흔히 여러분들중에서 키로거사용경험이 있으신분들은 smtp를 이용한 메
mawile.tistory.com
위키 문서
Gmail 세팅 글
Google - Gmail SMTP 사용을 위한 세팅 : 2025년 버전
Google - Gmail SMTP 사용을 위한 세팅 : 2025년 버전 SMTP를 빌려 사용하려면, 역시나 갓구글의 G메일이 가장 효율이 좋은 것 같습니다. 다만 불편한 것은 불친절한 사용 설명들이죠.지난 2023년 즈음에도
kincoding.com
'공부 > CS' 카테고리의 다른 글
| I/O Multiplexing IOCP 에코서버 만들기 with.C/C++ (0) | 2026.03.26 |
|---|---|
| libbcrypt 으로 비밀번호 해싱하기 with.C/C++ (0) | 2026.03.14 |
| SIGPIPE (0) | 2026.03.02 |
| openssl SSL_get_error 에러코드 with.C/C++ (0) | 2026.02.26 |
| 스레드 풀 thread pool (0) | 2026.02.24 |