Java实现一次性群发上百万封邮件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现一次性群发上百万封邮件相关的知识,希望对你有一定的参考价值。

公司最近要做群发邮件功能,是发给上百万客户;
可是这个群发量大会导致很多问题

1、海量邮件群发:要保证群发系统稳定性,能支持日处理百万级千万级的海量投递;

  2、IP进入黑名单:能处理IP地址或域名被邮件运营商列为黑名单问题;

  3、成功送达率:在发布内容合法的情况下,如何直达用户收件箱的问题;
我是想起个定时任务每天晚上执行,求大神给出技术解决方案

参考技术A

C++实现邮件群发的方法

这篇文章主要介绍了C++实现邮件群发的方法,较为详细的分析了邮件发送的原理与C++相关实现技巧,非常具有实用价值,需要的朋友可以参考下

本文实例讲述了C++实现邮件群发的方法。分享给大家供大家参考。具体如下:
关于生成随机QQ邮箱不精确的问题,在之后版本打算另写一个采集器插件进行帐号采集,所以,这个软件只用来进行内容发送,邮箱进行随机生成

如果你已经有采集来的QQ号,请复制到SendList.txt 替换内容即可
可以直接复制HTML代码到邮件内容,保存即可。目前邮件内容最大设置为10000字节,如果有增大的必要,欢迎提交留言。

这是我学习后VC编程中涉及到多线程,socket,及一些WINDOWS API的宗合应用
使用说明:

一、SMTP设置

1、SMTP设置中,收件箱地址:填写邮箱地址为帐号测试邮箱,可以填写你自己的邮箱作为接收测试。打星号为必填。邮箱帐号及密码,是即将用于群发的帐号和密码
2、群发发送时间间隔,默认为零,可以选择填写发送间隔时间,单位为秒,防止多次快速发送相同内容被停用。
3、勾选邮件标题后插入系统时间,可以在标题上加上系统时间
4、导入群发列表,勾选后,可以点击群发,暂停,继续。(群发列表为程序目录中SendList.txt)如果你有邮箱采集软件,可以把彩集的邮箱地址按照格式,一行一个,放入其中),没有的话,可以点击“收件箱”页面生成随机QQ邮箱

二、邮件内容

1、填写邮件标题,邮件内容可以为纯文本,也可以是HTML代码,附件目前只支持TXT文本。
2、群发前,请测试邮件内容,是否完整。附件是否正常接收。

三、收件箱

1、收件箱中顺序生成QQ邮箱,请不要超过6位数QQ号,位数过大,生成时间过长,容易造成假死。
2、群发前,先生成QQ邮箱列表。如果你有采集邮箱帐号,可以按格式放入SendList.txt 中

版本更新说明:

1、可增加多个附件。
2、修复标题插入日期造成程序崩溃的BUG。
3、增加发送间隔时间选项。
4、支持HTML代码
5、增加干预码

  1 #include "stdafx.h"
  2 #include <windows.h>
  3 #include <windowsx.h>
  4 #include "Resource.h"
  5 #include "TabDlg1.h"
  6 #include "winsock2.h"
  7 #define MAXSTRING 10000
  8 static int flag=0;     //标记是否群发
  9 TCHAR* pTitle = NULL;     //指向标题文件内容的指针
 10 HANDLE hThread=NULL;     //线程返回句柄
 11 long i=0;        //列表控件“行”计数
 12 static long sendNum=1;    //发送邮件数量
 13 #pragma comment(lib,"WSOCK32.LIB")
 14 extern TCHAR tcRunPath[MAX_PATH];  //程序当前路径目录
 15 extern TCHAR shortPath[MAX_PATH];  //文件路径,TAB2中的全局变量
 16 extern TCHAR titlePath[256];   //邮件标题文件路径
 17 extern TCHAR mtPath[256];    //邮件内容文件路径
 18 extern TCHAR sendListPath[MAX_PATH]; //发送列表文件路径
 19 extern BOOL attach;     //是否添加了附件
 20 extern TCHAR file[MAXSTRING];   //附件部份代码
 21 BOOL WINAPI TAB1_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 22 {
 23  switch(uMsg)
 24  {
 25   HANDLE_MSG(hWnd, WM_INITDIALOG, TAB1_OnInitDialog);
 26   HANDLE_MSG(hWnd, WM_COMMAND, TAB1_OnCommand);
 27   HANDLE_MSG(hWnd,WM_CLOSE, TAB1_OnClose);
 28  }
 29  return FALSE;
 30 }
 31 BOOL TAB1_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
 32 {
 33  InitLVColumn(hwnd);
 34  InitComboBox(hwnd);
 35  return TRUE;
 36 }
 37 void TAB1_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
 38 { 
 39  IsChecked(hwnd);   //判断勾选框是否被勾选
 40  IsRadioChecked(hwnd); //RADIO控件是否被选择
 41  switch(id)
 42  {
 43   case IDC_MAIL_TEST:
 44   {  
 45    if(IsDlgButtonChecked(hwnd,IDC_RADIO_QQ)!=BST_CHECKED && IsDlgButtonChecked(hwnd,IDC_RADIO_163)!=BST_CHECKED)
 46    {
 47     MessageBox(hwnd,TEXT("请选择发QQ邮箱或163信箱"),"",MB_OK);
 48     return;
 49    }
 50    else
 51    {
 52     flag=0;
 53     hThread=CreateThread(NULL,0,ThreadFunc,hwnd,0,0);
 54    }
 55   }
 56   break;
 57   case IDC_BUTTON_START:
 58   {  
 59    if(IsDlgButtonChecked(hwnd,IDC_RADIO_QQ)!=BST_CHECKED && IsDlgButtonChecked(hwnd,IDC_RADIO_163)!=BST_CHECKED)
 60    {
 61     MessageBox(hwnd,TEXT("请选择发QQ邮箱或163信箱"),"",MB_OK);
 62     return;
 63    }
 64    else
 65    {
 66     flag=1;
 67     hThread=CreateThread(NULL,0,ThreadFunc,hwnd,0,0);
 68    }
 69   }
 70   break;
 71   case IDC_BUTTON_PAUSE:
 72   {   
 73    if(NULL!=hThread)
 74    {
 75     SuspendThread(hThread); 
 76    }
 77    else
 78    {
 79     return;
 80    }
 81   }
 82   break;
 83   case IDC_BUTTON_RESUME:
 84   {   
 85    if(NULL!=hThread)
 86    {
 87     ResumeThread(hThread); 
 88    }
 89    else
 90    {
 91     return;
 92    }
 93   }
 94   break;
 95   default:
 96   break;
 97  }
 98 }
 99 void TAB1_OnClose(HWND hwnd)
100 {
101  EndDialog(hwnd, 0);
102 }
103 int InitLVColumn(HWND hwnd)    //列表初始化设置,视图选为报表视图
104 {
105  LVCOLUMN lvColumn;
106  MyLVColumn MyColumn[2] = {{TEXT("编号"), 0x30, LVCFMT_CENTER},{TEXT("邮箱帐号"), 0x99, LVCFMT_CENTER}};
107  lvColumn.mask = LVCF_TEXT|LVCF_FMT|LVCF_WIDTH|LVCF_SUBITEM;
108  DWORD dwStyle =ListView_GetExtendedListViewStyle(GetDlgItem(hwnd, IDC_LIST));   //得到列表当前拓展风格
109  dwStyle |= LVS_EX_FULLROWSELECT; //选中某行使整行高亮(只适用与report风格的listctrl)
110  dwStyle |= LVS_EX_GRIDLINES;  //网格线(只适用与report风格的listctrl)
111 // dwStyle |= LVS_EX_CHECKBOXES;  //item前生成checkbox控件
112  ListView_SetExtendedListViewStyle(GetDlgItem(hwnd, IDC_LIST),dwStyle);     //设置列表扩展风格
113  for(int i = 0; i < 2; i++)
114  {
115   lvColumn.pszText = MyColumn[i].szColumnName;
116   lvColumn.cx = MyColumn[i].cx;
117   lvColumn.fmt = MyColumn[i].fmt;
118   SendDlgItemMessage(hwnd, IDC_LIST, LVM_INSERTCOLUMN, i, (LPARAM)&lvColumn);
119  }
120   
121  return 0;
122 }
123 int InitComboBox(HWND hwnd)//窗口控件初始化设置
124 {
125  HWND hwndCombo=GetDlgItem(hwnd,IDC_COMBO_SLEEPTIME);
126  ComboBox_InsertString(hwndCombo,-1,TEXT("0"));
127  ComboBox_InsertString(hwndCombo,-1,TEXT("1"));
128  ComboBox_InsertString(hwndCombo,-1,TEXT("2"));
129  ComboBox_InsertString(hwndCombo,-1,TEXT("5"));
130  ComboBox_InsertString(hwndCombo,-1,TEXT("10"));
131  ComboBox_InsertString(hwndCombo,-1,TEXT("20"));
132  ComboBox_SetText(hwndCombo,TEXT("0"));
133  SetDlgItemText(hwnd,IDC_EDIT_MAILADD,TEXT("12345678@qq.com"));
134  ComboBox_InsertString(GetDlgItem(hwnd,IDC_COMBO_SMTP),0,TEXT("smtp.qq.com"));
135  ComboBox_InsertString(GetDlgItem(hwnd,IDC_COMBO_SMTP),1,TEXT("smtp.163.com"));
136  ComboBox_SetText(GetDlgItem(hwnd,IDC_COMBO_SMTP),TEXT("smtp.qq.com"));
137  SetDlgItemText(hwnd,IDC_EDIT_MAILPORT,TEXT("25")); 
138  SetDlgItemText(hwnd,IDC_EDIT_USERNAME,TEXT(""));
139   
140  return 1;
141 }
142 int IsChecked(HWND hwnd)  //checkbox未选中的设置为只读
143 {
144  if(IsDlgButtonChecked(hwnd,IDC_CHECK_QUNFA)!=BST_CHECKED)
145  {
146   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_START), false);
147   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_RESUME), false);
148   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_PAUSE), false);
149   return 0;
150  }
151  else
152  {
153   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_START), true);
154   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_RESUME), true);
155   EnableWindow(GetDlgItem(hwnd, IDC_BUTTON_PAUSE), true);
156   return 1;
157  }
158  return 0;
159 }
160 int IsRadioChecked(HWND hwnd) //RADIO控件被选中,则设置相应的SMTP 地址
161 {
162  if(IsDlgButtonChecked(hwnd,IDC_RADIO_QQ)==BST_CHECKED)
163  {
164   ComboBox_SetText(GetDlgItem(hwnd,IDC_COMBO_SMTP),TEXT("smtp.qq.com"));
165   return 1;
166  }
167  if(IsDlgButtonChecked(hwnd,IDC_RADIO_163)==BST_CHECKED)
168  {
169   ComboBox_SetText(GetDlgItem(hwnd,IDC_COMBO_SMTP),TEXT("smtp.163.com"));
170   return 2;
171  }
172  return 0;
173 }
174 TCHAR* readText1(HWND hwnd)    //读取文件--邮件发送内容
175 {
176  int nLen = 0;
177  FILE *pF = fopen(titlePath , "r"); //打开文件
178  fseek(pF, 0, SEEK_END);    //文件指针移到文件尾
179  nLen = ftell(pF);     //得到当前指针位置, 即是文件的长度
180  rewind(pF);       //文件指针恢复到文件头位置
181  pTitle = (char*) malloc(sizeof(char)*nLen+1);//动态申请空间, 为保存字符串结尾标志\\0, 多申请一个字符的空间
182  if(!pTitle)
183  {
184   MessageBox(hwnd,TEXT("内存不够!"),"错误",MB_ICONWARNING);
185   exit(0);
186  }
187  nLen = fread(pTitle, sizeof(char), nLen, pF);
188  pTitle[nLen] = \'\\0\';   //添加字符串结尾标志
189  if(IsDlgButtonChecked(hwnd,IDC_CHECK_TITLE)==BST_CHECKED) //是否勾选在邮件标题 加入系统当前时间
190  {
191   SYSTEMTIME stLocal;
192   GetLocalTime(&stLocal); //得到系统当前时间
193   TCHAR strTime[256];
194   ZeroMemory(strTime,sizeof(strTime)/sizeof(TCHAR));
195   wsprintf(strTime,"%04u-%02u-%02u %02u:%02u:%02u",stLocal.wYear,stLocal.wMonth,stLocal.wDay,stLocal.wHour,stLocal.wMinute,stLocal.wSecond);
196   strcat(pTitle,strTime); //在标题后加入系统时间
197  }
198  fclose(pF);     //关闭文件
199  TCHAR* pText = NULL;
200  pF = fopen(mtPath , "r"); //打开文件
201  fseek(pF, 0, SEEK_END); 
202  nLen = ftell(pF); 
203  rewind(pF); 
204  pText = (char*) malloc(sizeof(char)*nLen+1); //动态申请空间, 为保存字符串结尾标志\\0, 多申请一个字符的空间
205  if(!pText)
206  {
207   MessageBox(hwnd,TEXT("内存不够!"),"错误",MB_ICONWARNING);
208   exit(0);
209  }
210  nLen = fread(pText, sizeof(char), nLen, pF);
211  pText[nLen] = \'\\0\';   //添加字符串结尾标志
212  fclose(pF);     //关闭文件
213  TCHAR chText[MAXSTRING];  
214  TCHAR* encText=base64_encode(pText,strlen(pText));  
215  strcpy(chText,encText);  //BASE64加密结果
216  free(encText);    //释放指针
217  free(pText);     //释放空间
218  return chText;
219 }
220 DWORD WINAPI ThreadFunc(LPVOID lpParam)//发邮件线程
221 {
222  HWND hwnd=(HWND)lpParam;
223  TCHAR userName[256];    //用户帐号
224  TCHAR userPassWord[256];   //用户密码
225  GetDlgItemText(hwnd,IDC_EDIT_USERNAME,userName,sizeof(userName)/sizeof(TCHAR));
226  GetDlgItemText(hwnd,IDC_EDIT_USERPASSWORD,userPassWord,sizeof(userPassWord)/sizeof(TCHAR));
227  TCHAR *name=userName; 
228  int i = 0; 
229  int j = strlen(name); 
230  TCHAR *encName = base64_encode(name, j);    //给用户名base64加密编码  
231  ZeroMemory(userName,sizeof(userName)/sizeof(TCHAR)); 
232  wsprintf(userName,"%s\\n", encName);      //在加编码后加入回车符
233  TCHAR *passWord=userPassWord; 
234  int k = strlen(passWord); 
235  TCHAR *encPassWord = base64_encode(passWord, k);  //给用户密码base64加密编码    
236  // int len = strlen(enc); 
237  // TCHAR *dec = base64_decode(enc, len);    //反编 
238  ZeroMemory(userPassWord,sizeof(userPassWord)/sizeof(TCHAR)); 
239  wsprintf(userPassWord,"%s\\n", encPassWord);
240  /*ZeroMemory(userName,sizeof(userName)/sizeof(TCHAR)); 
241  wsprintf(userName,"\\ndecoded : %s", dec);
242  MessageBox(hwnd,userName,"",MB_OK);*/     //反编译用户名,如果需要,可以把用户名密码反编后发到指定邮箱。
243  free(encName);           //释放指针
244  free(encPassWord);
245  //free(dec); 
246  if(1==flag)            //全局标记,点击群发还是测试,1为群发,0为测试
247  {
248    HANDLE wFile;
249    int szId;
250    TCHAR ch;
251    TCHAR szState[256];
252    strcpy(sendListPath,tcRunPath);
253    strcat(sendListPath,"\\\\SendList.txt");   //拼接全路径及文件名
254    TCHAR *sFileName=sendListPath;
255    FILE *fp=fopen(sFileName,"r");
256    if(fp==NULL)
257    {
258     return FALSE;        //打开文件失败,则返回,不读取
259    }
260    fseek(fp, 0, SEEK_END);
261    int length = ftell(fp);      //length为0,则是空的
262    rewind(fp);         //把指针移回文件头部 还可以用 fseek(fp, 0, SEEK_SET);效果一样
263    if(length==0)         //判断文件如果为空,则关闭文件,返回,
264    {
265     fclose(fp);        //要关闭打开的文件,不然退出时,无法保存
266     return FALSE;
267    }
268    while(!feof(fp))
269    { 
270     fscanf(fp,"%s\\r\\n",szState);
271     SocketQ(hwnd,userName,userPassWord,szState);
272     TCHAR sTime[256];    
273     unsigned long iTime;
274     ComboBox_GetText(GetDlgItem(hwnd,IDC_COMBO_SLEEPTIME),sTime,sizeof(sTime)/sizeof(TCHAR));
275     iTime=atoi(sTime);
276     iTime=iTime*1000;
277     Sleep(iTime);
278    } 
279    fclose(fp);
280  }
281  else
282  {
283   Socket(hwnd,userName,userPassWord); 
284  }
285  CloseHandle(hThread); 
286  return 0;
287 }
288 void SocketQ(HWND hwnd,TCHAR userName[],TCHAR userPassWord[],TCHAR szState[])//传入加密的用户名跟密码连接163
289 {
290    /*******************************************************************
291    使用Socket的程序在使用Socket之前必须调用WSAStartup函数。
292    该函数的第一个参数指明程序请求使用的Socket版本,
293    其中高位字节指明副版本、低位字节指明主版本;
294    操作系统利用第二个参数返回请求的Socket的版本信息。
295    当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,
296    然后绑定找到的Socket库到该应用程序中。
297    以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。
298    该函数执行成功后返回0。
299    *****************************************************************/
300   TCHAR* pText;
301   pText = readText1(hwnd);
302   int WSA_return;
303   WSADATA wsaData;   
304   WSA_return=WSAStartup(MAKEWORD(2,0),&wsaData);   //初始化Socket库  
305   SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//创建SOCKET
306   hostent* host = NULL; 
307   SOCKADDR_IN sa;
308   sa.sin_family=AF_INET;         //设置电线连接服务器端的端口
309   TCHAR cPort[100];
310   GetDlgItemText(hwnd,IDC_EDIT_MAILPORT,cPort,sizeof(cPort)/sizeof(TCHAR));
311   int iPort=atoi(cPort);
312   sa.sin_port = htons(iPort);   
313   //sa.sin_addr.S_un.S_addr = inet_addr("123.58.178.203");//可以写死IP地址   
314    HOSTENT *host_entry;         //存放主机域名,如smtp.qq.com
315    TCHAR host_name[256]="";
316    ZeroMemory(host_name,sizeof(host_name)/sizeof(TCHAR)); 
317    ComboBox_GetText(GetDlgItem(hwnd,IDC_COMBO_SMTP),host_name,sizeof(host_name)/sizeof(TCHAR));
318    TCHAR str_ipAdd[256];
319    if(WSA_return==0)
320    {
321    host_entry=gethostbyname(host_name);     // 要解析的域名或主机名 
322    if(host_entry!=NULL)
323    {
324    wsprintf(str_ipAdd,"%d.%d.%d.%d",
325     (host_entry->h_addr_list[0][0]&0x00ff),
326     (host_entry->h_addr_list[0][1]&0x00ff),
327     (host_entry->h_addr_list[0][2]&0x00ff),
328     (host_entry->h_addr_list[0][3]&0x00ff));
329      
330    }
331    }
332   sa.sin_addr.S_un.S_addr = inet_addr(str_ipAdd);   //得到域名IP地址
333   if(connect(sock,(SOCKADDR *)&sa,sizeof(sa))==SOCKET_ERROR)
334 有没有邮件群发工具推荐?

邮件群发哪个数量多一些?

161013java实现邮件群发带附件

为什么Goroutine能有上百万个,Java线程却只能有上千个?

哪些营销邮件平台比较靠谱?

java如何实现批量发送邮件