异常信息:
org.apache.commons.mail.EmailException: Sending the email to the following server failed : smtp.163.com:25
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:873)
at org.apache.commons.mail.Email.send(Email.java:898)
at com.ufida.thread.SendMailThread.sendMail(SendMailThread.java:126)
at com.ufida.thread.SendMailThread.run(SendMailThread.java:48)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
Caused by: javax.mail.MessagingException: Could not connect to SMTP host: smtp.163.com, port: 25, response: -1
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1533)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:453)
at javax.mail.Service.connect(Service.java:313)
at javax.mail.Service.connect(Service.java:172)
at javax.mail.Service.connect(Service.java:121)
at javax.mail.Transport.send0(Transport.java:190)
at javax.mail.Transport.send(Transport.java:120)
at org.apache.commons.mail.Email.sendMimeMessage(Email.java:863)
... 6 more
按道理说如果是不能连接smtp主机的的话,应该一封邮件都发不出的啊,怎么会发了几百封之后再报这样的错呢??用单线程也是一样!!
请大家帮帮忙!!谢谢。。。
15 个解决方案
#1
这个应该是163的邮件服务器禁止你再发送邮件了。防止发送大量的垃圾邮件。
除非你自己有邮件服务器。
除非你自己有邮件服务器。
#2
我用公司内部的邮箱也会发生连接不上的错误,用公司内部邮箱的时候大概还剩几百条的时候就报错误了。。。
#3
下面是启用线程的主要代码:
ExecutorService execpool = Executors.newFixedThreadPool(Constant.THREAD_NUM);
int totalNum = map.entrySet().size(); //map中的总数量
CountDownLatch threadSingal = new CountDownLatch(totalNum %Constant.READ_NUM == 0 ? totalNum /Constant.READ_NUM : totalNum /Constant.READ_NUM+1);
if(totalNum > 0){
flag = true;
}
for(int i = 0;i<totalNum;i++){
if(i == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn)); //启动线程线程
} else if(i%Constant.READ_NUM == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
}
}
threadSingal.await();
#4
如果你用的是javamail的话,他在创建session时好像有个debug的属性,把他设置成true看看,具体信息。不过我还是认为是邮件服务器拒绝发送邮件了,防止垃圾邮件。我估计你们公司的邮件服务器也对邮件的批量发送进行了控制的。如果不限制的话,一个用户发送几万带超大附件的邮件,那不把服务器给搞挂??
#5
线程类:
public class SendMailThread implements Runnable {
private Map<String,List<String>> map;
private int begin;
private int end;
private CountDownLatch threadSingal;
private Connection sqlConn;
public SendMailThread() {}
public SendMailThread(Map<String,List<String>> map, int begin, int end,CountDownLatch threadSingal, Connection sqlConn){
this.map = map;
this.begin = begin;
this.end = end;
this.threadSingal = threadSingal;
this.sqlConn = sqlConn;
}
@Override
public void run() {
Object[] obj = map.entrySet().toArray();
//System.out.println(Thread.currentThread().getName()+";"+begin+"-"+end);
for(int i = begin;i<end;i++){
Map.Entry<String, List<String>> temp = (Map.Entry<String, List<String>>)obj[i];
sendMail(temp);
}
threadSingal.countDown(); //减少CountDownLatch中的数
}
private void sendMail(Map.Entry<String, List<String>> temp){
try {
String key = temp.getKey();
List<String> value = temp.getValue();
String[] personinfo = key.split(":");
String tmpTitle = personinfo[0];
String tmpPsncode = personinfo[1];
String tmpVacccode = personinfo[2];
String tmpVpaycomment = personinfo[3]; //备注
String tmpClassid = personinfo[4]; //类别id
String tmpEmail = "wlxtaking@163.com"; //用来测试的邮箱 如果上线的话,把这个注释掉,把下面的代码打开就可以了
// String tmpEmail = personinfo[5]; //邮箱地址
String tmpDeptname = personinfo[6]; //部门名称
String tmpPsnname = personinfo[7]; //人名
StringBuffer itemnamesStr = new StringBuffer(""); //用来放薪资项名
List<String> nameList = new ArrayList<String>(); //薪资项名称
List<Double> valueList = new ArrayList<Double>(); //薪资项对应的费用
for(int i= 0 ;i<value.size();i++){
String tempValue = value.get(i);
String[] valuArr = tempValue.split(":");
nameList.add(valuArr[0]);
valueList.add(Double.parseDouble(valuArr[1]));
}
StringBuffer sendbody=new StringBuffer("<html><style type='text/css'>td{ background-color:#FFFFFF}</style><body><table bgcolor='#996699' width='1600px'><tr><td bgcolor='#9966FF' colspan='").append(5+nameList.size()).append("' align='center'><font color='#00CCFF' size='+5'><b>工资发放信息</b></font></td></tr><tr bgcolor='#CC0066'><td>标题</td><td>姓名</td><td>部门</td>");
for(int i = 0; i<nameList.size();i++){ //组装发送邮件标题内容
String vname = nameList.get(i);
sendbody.append("<td>").append(vname).append("</td>");
itemnamesStr.append("'").append(vname).append("'").append(",");
}
itemnamesStr.deleteCharAt(itemnamesStr.lastIndexOf(","));
sendbody.append("<td>银行账号</td><td>备注</td>");
sendbody.append("</tr><tr>");
sendbody.append("<td>").append(tmpTitle).append("</td>");
sendbody.append("<td>").append(tmpPsnname).append("</td>");
sendbody.append("<td>").append(tmpDeptname).append("</td>");
for(int i = 0;i<valueList.size();i++) { //组装每个表格的标题对应的值
Double money = valueList.get(i); sendbody.append("<td>").append(money.doubleValue()).append("</td>");
}
sendbody.append("<td>").append(tmpVacccode).append("</td>");
sendbody.append("<td>").append(tmpVpaycomment).append("</td>");
sendbody.append("</tr></table></body></html>");
/********************开始发送邮件内容**************************/
if(tmpEmail != null && !"".equals(tmpEmail) && tmpEmail.indexOf("@") > 0){ // 邮件必须包含@字符
/*************************/
String username = "xxxxx";
String password = "xxxxx";
String host = "smtp.163.com";
String from = "xxxxx@163.com";
htmlEmail.setAuthentication(username, password);
htmlEmail.setHostName(host);
// email.addTo(to, from);
htmlEmail.addTo("xxxx@sina.com");
htmlEmail.setFrom(from);
/************************/
htmlEmail.setSubject("xxxxxx");
htmlEmail.setHtmlMsg(sendbody.toString());
htmlEmail.setCharset("GB2312");
htmlEmail.send();
}
this.changeSendflag(tmpTitle, tmpPsncode, tmpClassid, itemnamesStr.toString(),sqlConn);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (EmailException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
}
/**
* 更新邮件表 bd_email_info中的sendflag字段
* @param title
* @param psncode
* @param classid
* @param itemname
* @throws MyException
*/
public void changeSendflag(String title,String psncode,String classid, String itemnamesStr,Connection sqlConn) throws MyException{
PreparedStatement pstmt = null;
String sql = "update bd_email_info set sendflag = 1 where title = '"+title+"' and psncode = '"+psncode+"' and classid = '"+classid+"' and itemname in ("+itemnamesStr+")";
try {
pstmt = sqlConn.prepareStatement(sql);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new MyException(e.getMessage());
} finally {
try {
if(pstmt != null){
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
#6
for(int i = 0;i<totalNum;i++){
if(i == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn)); //启动线程线程
} else if(i%Constant.READ_NUM == 0){//假设Constant.READ_NUM=3,当i=1,2时,执行哪条语句??好像没有语句可以执行。
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
}
}
#7
先谢谢duqiangcise的回答。 那我想请问一下您,那遇到这种情况的时候该如何解决问题呢??
#8
execpool.execute(Runnalbe xxx);这个是用来启用线程的。。
#9
那按照你现在的代码来理解的话,只有当i=0,或i是Constant.READ_NUM的倍数时才启动线程???
#10
恩,是的,因为我要将map中的数据按Constant.READ_NUM分成几组,然后每一组用一个线程发送啊。。所以。。我就只要当i等于Constant.READ_NUM倍数的时候才启用线程!!!
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
红色部分就是用来将数据分组了。。
#11
那就不清楚了。你可以这样测试一下:
1.把调用发送邮件的那个方法注释掉,在相同的地方把你要发送的邮件内容打印出来,看打印出的邮件数是否和你要发送的邮件数一致。如果一致的话,那可能就是邮件服务器对发送邮件数进行了限制,如果不一致的话,那可能就是别的问题了。
2.请问当你发送了多少封邮件后,产生的这个异常??如果每次在产生异常前发送的邮件数目一定的话,那就更有可能是邮件服务器的限制了
1.把调用发送邮件的那个方法注释掉,在相同的地方把你要发送的邮件内容打印出来,看打印出的邮件数是否和你要发送的邮件数一致。如果一致的话,那可能就是邮件服务器对发送邮件数进行了限制,如果不一致的话,那可能就是别的问题了。
2.请问当你发送了多少封邮件后,产生的这个异常??如果每次在产生异常前发送的邮件数目一定的话,那就更有可能是邮件服务器的限制了
#12
谢谢您的回答。。对于第一个问题,他们的数量是一样的,刚刚测了一下。
第二个问题:每次都是到了发送了差不多数量的邮件的时候就开始报异常了。。。那我明天的时候去问下那边是不是有把邮件服务器限制。。
#13
不是邮件发送数目有限制。。。后来我是通过将发邮件的程序做成定时的去做。。。。。这样就可以将邮件全部发送完了。。
#14
有什么区别???定不定时,只是实现方式上的差别而已。
#15
这里smtp服务器如果一下子发太多邮件的话,有时会报连接不上的错误,我用一个标识来表示是否已经发送了。。这样如果发送了的话就把标识至为1,因为程序是定时,这样没有发送的邮件下次发的时候就可以发了。
#1
这个应该是163的邮件服务器禁止你再发送邮件了。防止发送大量的垃圾邮件。
除非你自己有邮件服务器。
除非你自己有邮件服务器。
#2
我用公司内部的邮箱也会发生连接不上的错误,用公司内部邮箱的时候大概还剩几百条的时候就报错误了。。。
#3
下面是启用线程的主要代码:
ExecutorService execpool = Executors.newFixedThreadPool(Constant.THREAD_NUM);
int totalNum = map.entrySet().size(); //map中的总数量
CountDownLatch threadSingal = new CountDownLatch(totalNum %Constant.READ_NUM == 0 ? totalNum /Constant.READ_NUM : totalNum /Constant.READ_NUM+1);
if(totalNum > 0){
flag = true;
}
for(int i = 0;i<totalNum;i++){
if(i == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn)); //启动线程线程
} else if(i%Constant.READ_NUM == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
}
}
threadSingal.await();
#4
如果你用的是javamail的话,他在创建session时好像有个debug的属性,把他设置成true看看,具体信息。不过我还是认为是邮件服务器拒绝发送邮件了,防止垃圾邮件。我估计你们公司的邮件服务器也对邮件的批量发送进行了控制的。如果不限制的话,一个用户发送几万带超大附件的邮件,那不把服务器给搞挂??
#5
线程类:
public class SendMailThread implements Runnable {
private Map<String,List<String>> map;
private int begin;
private int end;
private CountDownLatch threadSingal;
private Connection sqlConn;
public SendMailThread() {}
public SendMailThread(Map<String,List<String>> map, int begin, int end,CountDownLatch threadSingal, Connection sqlConn){
this.map = map;
this.begin = begin;
this.end = end;
this.threadSingal = threadSingal;
this.sqlConn = sqlConn;
}
@Override
public void run() {
Object[] obj = map.entrySet().toArray();
//System.out.println(Thread.currentThread().getName()+";"+begin+"-"+end);
for(int i = begin;i<end;i++){
Map.Entry<String, List<String>> temp = (Map.Entry<String, List<String>>)obj[i];
sendMail(temp);
}
threadSingal.countDown(); //减少CountDownLatch中的数
}
private void sendMail(Map.Entry<String, List<String>> temp){
try {
String key = temp.getKey();
List<String> value = temp.getValue();
String[] personinfo = key.split(":");
String tmpTitle = personinfo[0];
String tmpPsncode = personinfo[1];
String tmpVacccode = personinfo[2];
String tmpVpaycomment = personinfo[3]; //备注
String tmpClassid = personinfo[4]; //类别id
String tmpEmail = "wlxtaking@163.com"; //用来测试的邮箱 如果上线的话,把这个注释掉,把下面的代码打开就可以了
// String tmpEmail = personinfo[5]; //邮箱地址
String tmpDeptname = personinfo[6]; //部门名称
String tmpPsnname = personinfo[7]; //人名
StringBuffer itemnamesStr = new StringBuffer(""); //用来放薪资项名
List<String> nameList = new ArrayList<String>(); //薪资项名称
List<Double> valueList = new ArrayList<Double>(); //薪资项对应的费用
for(int i= 0 ;i<value.size();i++){
String tempValue = value.get(i);
String[] valuArr = tempValue.split(":");
nameList.add(valuArr[0]);
valueList.add(Double.parseDouble(valuArr[1]));
}
StringBuffer sendbody=new StringBuffer("<html><style type='text/css'>td{ background-color:#FFFFFF}</style><body><table bgcolor='#996699' width='1600px'><tr><td bgcolor='#9966FF' colspan='").append(5+nameList.size()).append("' align='center'><font color='#00CCFF' size='+5'><b>工资发放信息</b></font></td></tr><tr bgcolor='#CC0066'><td>标题</td><td>姓名</td><td>部门</td>");
for(int i = 0; i<nameList.size();i++){ //组装发送邮件标题内容
String vname = nameList.get(i);
sendbody.append("<td>").append(vname).append("</td>");
itemnamesStr.append("'").append(vname).append("'").append(",");
}
itemnamesStr.deleteCharAt(itemnamesStr.lastIndexOf(","));
sendbody.append("<td>银行账号</td><td>备注</td>");
sendbody.append("</tr><tr>");
sendbody.append("<td>").append(tmpTitle).append("</td>");
sendbody.append("<td>").append(tmpPsnname).append("</td>");
sendbody.append("<td>").append(tmpDeptname).append("</td>");
for(int i = 0;i<valueList.size();i++) { //组装每个表格的标题对应的值
Double money = valueList.get(i); sendbody.append("<td>").append(money.doubleValue()).append("</td>");
}
sendbody.append("<td>").append(tmpVacccode).append("</td>");
sendbody.append("<td>").append(tmpVpaycomment).append("</td>");
sendbody.append("</tr></table></body></html>");
/********************开始发送邮件内容**************************/
if(tmpEmail != null && !"".equals(tmpEmail) && tmpEmail.indexOf("@") > 0){ // 邮件必须包含@字符
/*************************/
String username = "xxxxx";
String password = "xxxxx";
String host = "smtp.163.com";
String from = "xxxxx@163.com";
htmlEmail.setAuthentication(username, password);
htmlEmail.setHostName(host);
// email.addTo(to, from);
htmlEmail.addTo("xxxx@sina.com");
htmlEmail.setFrom(from);
/************************/
htmlEmail.setSubject("xxxxxx");
htmlEmail.setHtmlMsg(sendbody.toString());
htmlEmail.setCharset("GB2312");
htmlEmail.send();
}
this.changeSendflag(tmpTitle, tmpPsncode, tmpClassid, itemnamesStr.toString(),sqlConn);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (EmailException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
}
/**
* 更新邮件表 bd_email_info中的sendflag字段
* @param title
* @param psncode
* @param classid
* @param itemname
* @throws MyException
*/
public void changeSendflag(String title,String psncode,String classid, String itemnamesStr,Connection sqlConn) throws MyException{
PreparedStatement pstmt = null;
String sql = "update bd_email_info set sendflag = 1 where title = '"+title+"' and psncode = '"+psncode+"' and classid = '"+classid+"' and itemname in ("+itemnamesStr+")";
try {
pstmt = sqlConn.prepareStatement(sql);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
throw new MyException(e.getMessage());
} finally {
try {
if(pstmt != null){
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
#6
for(int i = 0;i<totalNum;i++){
if(i == 0){
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn)); //启动线程线程
} else if(i%Constant.READ_NUM == 0){//假设Constant.READ_NUM=3,当i=1,2时,执行哪条语句??好像没有语句可以执行。
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
}
}
#7
先谢谢duqiangcise的回答。 那我想请问一下您,那遇到这种情况的时候该如何解决问题呢??
#8
execpool.execute(Runnalbe xxx);这个是用来启用线程的。。
#9
那按照你现在的代码来理解的话,只有当i=0,或i是Constant.READ_NUM的倍数时才启动线程???
#10
恩,是的,因为我要将map中的数据按Constant.READ_NUM分成几组,然后每一组用一个线程发送啊。。所以。。我就只要当i等于Constant.READ_NUM倍数的时候才启用线程!!!
execpool.execute(new SendMailThread(map, i, i+Constant.READ_NUM >= totalNum ? totalNum : i+Constant.READ_NUM,threadSingal,sqlConn));
红色部分就是用来将数据分组了。。
#11
那就不清楚了。你可以这样测试一下:
1.把调用发送邮件的那个方法注释掉,在相同的地方把你要发送的邮件内容打印出来,看打印出的邮件数是否和你要发送的邮件数一致。如果一致的话,那可能就是邮件服务器对发送邮件数进行了限制,如果不一致的话,那可能就是别的问题了。
2.请问当你发送了多少封邮件后,产生的这个异常??如果每次在产生异常前发送的邮件数目一定的话,那就更有可能是邮件服务器的限制了
1.把调用发送邮件的那个方法注释掉,在相同的地方把你要发送的邮件内容打印出来,看打印出的邮件数是否和你要发送的邮件数一致。如果一致的话,那可能就是邮件服务器对发送邮件数进行了限制,如果不一致的话,那可能就是别的问题了。
2.请问当你发送了多少封邮件后,产生的这个异常??如果每次在产生异常前发送的邮件数目一定的话,那就更有可能是邮件服务器的限制了
#12
谢谢您的回答。。对于第一个问题,他们的数量是一样的,刚刚测了一下。
第二个问题:每次都是到了发送了差不多数量的邮件的时候就开始报异常了。。。那我明天的时候去问下那边是不是有把邮件服务器限制。。
#13
不是邮件发送数目有限制。。。后来我是通过将发邮件的程序做成定时的去做。。。。。这样就可以将邮件全部发送完了。。
#14
有什么区别???定不定时,只是实现方式上的差别而已。
#15
这里smtp服务器如果一下子发太多邮件的话,有时会报连接不上的错误,我用一个标识来表示是否已经发送了。。这样如果发送了的话就把标识至为1,因为程序是定时,这样没有发送的邮件下次发的时候就可以发了。