Java实现一次性群发上百万封邮件
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现一次性群发上百万封邮件相关的知识,希望对你有一定的参考价值。
公司最近要做群发邮件功能,是发给上百万客户;
可是这个群发量大会导致很多问题
1、海量邮件群发:要保证群发系统稳定性,能支持日处理百万级千万级的海量投递;
2、IP进入黑名单:能处理IP地址或域名被邮件运营商列为黑名单问题;
3、成功送达率:在发布内容合法的情况下,如何直达用户收件箱的问题;
我是想起个定时任务每天晚上执行,求大神给出技术解决方案
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 有没有邮件群发工具推荐?