一、相关概念
QQ邮箱关于三种协议的解释:SMTP/IMAP服务
1.SMTP协议
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是一种用于发送电子邮件的互联网标准。它在TCP/IP协议族中,通常使用25端口进行通信。SMTP协议允许邮件发送者和接收者之间的邮件传输,并且可以通过中继器或网关实现不同网络之间的邮件传输。SMTP的一个重要特性是它能够跨越网络传输邮件,这被称为“SMTP邮件中继”。
2.POP3协议
POP3 (Post Office Protocol - Version 3)协议用于支持使用电子邮件客户端获取并删除在服务器上的电子邮件。
POP允许电子邮件客户端下载服务器上的邮件,但是你在电子邮件客户端上的操作(如:移动邮件、标记已读等)不会反馈到服务器上的,比如:你通过电子邮件客户端收取了QQ邮箱中的3封邮件并移动到了其他文件夹,这些移动动作是不会反馈到服务器上的,也就是说,QQ邮箱服务器上的这些邮件是没有同时被移动的。需要特别注意的是,第三方客户端通过POP收取邮件时,也是有可能同步删除服务端邮件。
3.IMAP协议
IMAP(Internet Message Access Protocol,互联网邮件访问协议)是一个应用层协议,用于从本地邮件客户端(如Microsoft Outlook、Outlook Express、Foxmail、Mozilla Thunderbird)访问远程服务器上的邮件。
在IMAP协议上,电子邮件客户端的操作都会反馈到服务器上,你对邮件进行的操作(如:移动邮件、标记已读、删除邮件等)服务器上的邮件也会做相应的动作。也就是说,IMAP 是“双向”的。
4.守护进程
守护进程是后台运行的无终端关联的系统进程,常在启动时启动,提供持续服务,如网络服务、日志记录和定时任务。其特点包括脱离终端、后台运行、持久服务、资源管理和错误处理。创建守护进程涉及重定向文件描述符、创建新会话、改变工作目录等步骤。`ps` 和 `top` 命令用于查看守护进程,前者提供进程快照,后者显示实时资源使用情况。
二、实践
1.telnet登录QQ发送邮件
打开PowerShell,输入下面命令打开telnet
telnet smtp.qq.com 587
如果无法识别telnet,打开控制面板,点击卸载程序,点击左侧的 “启用或关闭 Windows 功能”
找到 “Telnet 客户端”,勾选它,然后点击确定
等待安装完成,重新打开 PowerShell 或 CMD
按照以下步骤发送邮件
telnet smtp.qq.com 587 (qq邮箱说明:http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=369)
Ehlo qq.com (打招呼)
STARTTLS (开启加密)
auth login (输入登陆命令)
//输入邮箱账号(Base64形式)(回车)
//输入邮箱授权码(Base64形式)(回车)
235 Authentication successful(提示登陆成功)
Mail from:XXXX@qq.com (注意冒号后面别有空格)
250 Ok
Rcpt to:XXX@qq.com (注意冒号后面别有空格)
250 Ok
DATA (输入此命令开始写信)
FROM:XXX@qq.com
TO:XXX@qq.com
SUBJECT:
(空一行)
内容
. (“.”结束)
quit
2.守护进程实现
(1)阿里云守护进程
首先登录QQ邮箱,点击左下方账号与安全中心,点击安全设置,点击生成授权码
在代码中需要用到你的qq邮箱地址(账号@qq.com)和授权码的base64形式,可到此处转换:https://base64.us/
进入在linux系统,输入以下命令,创建.c文件
vim smtp_daemon.c
守护进程代码如下(C语言):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <time.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define SMTP_SERVER "smtp.qq.com"
#define SMTP_PORT 465 // 使用SSL加密端口
#define EMAIL_FROM "你的qq邮箱地址"
#define EMAIL_PASS "邮箱密码或授权码" // 确认这是最新的授权码
#define EMAIL_TO "实际接收邮箱"
SSL_CTX* init_ssl() {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
return SSL_CTX_new(SSLv23_client_method());
}
void send_email() {
SSL_CTX* ctx = init_ssl();
int sockfd;
struct hostent *server;
struct sockaddr_in serv_addr;
char buffer[1024];
// 创建socket连接
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server = gethostbyname(SMTP_SERVER);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SMTP_PORT);
memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection failed");
return;
}
// SSL握手
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockfd);
if (SSL_connect(ssl) <= 0) {
ERR_print_errors_fp(stderr);
return;
}
// SMTP协议交互
SSL_read(ssl, buffer, sizeof(buffer)); // 读取欢迎消息
sprintf(buffer, "EHLO localhost\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
sprintf(buffer, "AUTH LOGIN\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
// 发送base64编码
sprintf(buffer, "实际base64编码\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
sprintf(buffer, "实际base64编码\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
// 设置发件人和收件人
sprintf(buffer, "MAIL FROM:<%s>\r\n", EMAIL_FROM);
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
sprintf(buffer, "RCPT TO:<%s>\r\n", EMAIL_TO);
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
// 发送邮件内容
sprintf(buffer, "DATA\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
time_t now;
time(&now);
sprintf(buffer,
"From: <%s>\r\n"
"To: <%s>\r\n"
"Subject: 守护进程状态\r\n"
"\r\n"
"守护进程正在运行中....\r\n"
"当前时间: %s\r\n"
".\r\n",
EMAIL_FROM, EMAIL_TO, ctime(&now));
SSL_write(ssl, buffer, strlen(buffer));
SSL_read(ssl, buffer, sizeof(buffer));
sprintf(buffer, "QUIT\r\n");
SSL_write(ssl, buffer, strlen(buffer));
SSL_free(ssl);
close(sockfd);
SSL_CTX_free(ctx);
}
void daemonize() {
pid_t pid = fork();
if (pid < 0) exit(1);
if (pid > 0) exit(0);
setsid();
chdir("/");
umask(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
int main() {
daemonize();
while(1) {
send_email();
sleep(60);
}
return 0;
}
其中邮箱地址以及授权码内容替换为你自己的
编译此文件
gcc smtp_daemon.c -o smtp_daemon -lssl -lcrypto
运行守护程序:
./smtp_daemon
运行结果如下图:
停止命令:
pkill -f smtp_daemon
(2)树莓派守护进程
登录树莓派,创建serial_daemon.c文件,代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <signal.h>
#define SERIAL_PORT "/dev/serial0" // 树莓派默认串口设备
#define BAUD_RATE B115200 // 波特率115200
#define MESSAGE "守护进程正在运行中....\r\n"
#define INTERVAL 10 // 间隔10秒
int serial_fd = -1;
// 初始化串口
int init_serial() {
serial_fd = open(SERIAL_PORT, O_RDWR | O_NOCTTY | O_NDELAY);
if (serial_fd == -1) {
perror("无法打开串口");
return -1;
}
struct termios options;
tcgetattr(serial_fd, &options);
// 设置波特率
cfsetispeed(&options, BAUD_RATE);
cfsetospeed(&options, BAUD_RATE);
// 8N1配置
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
// 禁用硬件流控
options.c_cflag &= ~CRTSCTS;
// 启用接收
options.c_cflag |= CREAD | CLOCAL;
// 禁用软件流控
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// 原始输入模式
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// 原始输出模式
options.c_oflag &= ~OPOST;
// 设置超时
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(serial_fd, TCSANOW, &options);
return 0;
}
// 信号处理函数
void signal_handler(int sig) {
if (serial_fd != -1) {
close(serial_fd);
}
exit(0);
}
// 守护进程化
void daemonize() {
pid_t pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS); // 父进程退出
}
// 子进程继续
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
// 忽略终端I/O信号
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGHUP, SIG_IGN);
// 再次fork确保不是会话组长
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS); // 父进程退出
}
// 设置文件权限掩码
umask(0);
// 更改工作目录
chdir("/");
// 关闭所有打开的文件描述符
for (int x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
close(x);
}
}
int main() {
// 注册信号处理
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
// 守护进程化
daemonize();
// 初始化串口
if (init_serial() < 0) {
return -1;
}
// 主循环
while (1) {
write(serial_fd, MESSAGE, strlen(MESSAGE));
sleep(INTERVAL);
}
close(serial_fd);
return 0;
}
Ctrl+O->Enter-> Ctrl+X保存并退出
编译程序:
gcc serial_daemon.c -o serial_daemon
运行守护进程:
sudo ./serial_daemon
结果如下:
PS:串口配置波特率为115200,接受模式为文本模式,文本编码为UTF-8,不然可能会乱码
三、总结
本次学习了如何使用telnet登录QQ发送邮件,这一步除了挺多的错,找了一些博客看其中的方法也不行,老是断连。参考文章为Email-FTP-RTSP协议实践研究_rtp邮箱-CSDN博客,还有阿里云上编写一个守护进程程序以及树莓派守护进程,都是问的deepseek,不对的话多问几次就对了。重点是我第一次发现代码可以直接在Putty上粘贴!!!!!(复制之后在Putty端右键点击就粘贴上去了),那我一个一个敲了那么多周算什么???命比苦瓜还苦。。。